2009-12-05 1 views
4

В моем приложении я должен получить некоторые классы из базового, проблема в том, что я хочу, чтобы принудительно применяла производную классификацию для реализации 3 конкретных конструкторов. Поскольку у C++ нет виртуального чистого конструктора, он казался довольно отчаянным (мне пришлось вручную проверять каждую реализацию класса, чтобы убедиться, что конкретные ctors реализованы, а не забавно делать).имитировать виртуальный конструктор в C++

Вчера я нашел сумасшедший способ имитировать поведение виртуального CTOR:

 


template <class T> 
class AbstractEnforcer{ 
    protected: 
    AbstractEnforcer(){} 
    private: 
    static void Enforcer(){ 
     delete new T(); 
     delete new T(*(new unsigned int)); 
     delete new T(*(new unsigned int, *(new QString)); 
    } 
    } 

class AbstractClass : private AbstractEnforcer<AbstractClass>{ 

} 
 

единственное неудобство этого подхода заключается в том, что я должен объявить все производный класс с синтаксисом:


class X : private AbstractEnforcer<X> 

И даже если это не проблема; потому что метод Enforcer() никогда не называется (и даже так он ничего не делает [надеюсь, что так !!!])

Мой вопрос: «есть ли какое-либо среднее значение (не с макросами), чтобы заставить производный класс использовать этот механизм не parametrazing в AbstractClass (потому что это будет работать только один уровень вывода»


template <class T> 
class AbstractClass : private AbstractEnforcer<T>{ 

} 

ответ

4

Ваше решение не решает проблему, так как шаблонный код, который Isnt используется не экземпляр, и, таким образом, если вы вручную вызвать эту функцию - он не будет проверять наличие требуемых конструкторов.

W шляпа вы можете сделать, это этот метод вызывается из конструктора вашего Инфорсер:

template <class T> 
class AbstractEnforcer{ 
    protected: 
     AbstractEnforcer(){ Enforcer(); } 
    private: 
     static void Enforcer(){ 
      delete new T(); 
      delete new T(*(new unsigned int)); 
      delete new T(*(new unsigned int, *(new QString))); 
     } 
     // disable: 
     AbstractEnforcer (const AbstractEnforcer &enf); 
}; 

class AbstractClass : private AbstractEnforcer<AbstractClass>{ 

}; 

int main() { 
    AbstractClass c; 
} 

Затем компилятор жалуется - миссия выполнена.

Обратите внимание, что я отключил конструктор копирования, чтобы исключить эту проверку (путем вызова другого конструктора).

Edit - Non протечка Enforcer(): [Как нет Absolutly нет необходимости использовать динамическое распределение там ..]

 static void Enforcer(){ 
      T t1(); 
      T t2(int(3)); 
      T t3(int(4), QString()); 
     } 
+0

В действительности я определил функцию принудительного выполнения в другом файле cpp, и проверка статического конструктора работает неплохо, но я признаю, что это лучший способ сделать это (я использую gcc-4.x) t знать, работает ли это с другими компиляторами. – chedi

+0

Проблема заключается в том, когда я хочу получить класс a из AbstractClass, компилятор не применяет проверку прототипа, поскольку AbstractClass наследует AbstractEnforcer :: Enforcer(), и нет автоматического средства для проверки на AbstractEnforcer chedi

+0

Вам придется наследовать каждый новый класс, которого я боюсь, поскольку эта функция должна пытаться вызвать конструкторы каждого класса, а не только конструкторы непосредственно наследуемого от него. Но все будет хорошо. – rmn

3

См this page в FAQ C++.

Что я хотел бы сделать что-то вроде этого:

 
class AbstractClass { 
    public: 
     virtual AbstractClass* create() const = 0; 
     virtual AbstractClass* create(unsigned int) const = 0; 
     virtual AbstractClass* create(unsigned int, QString) const = 0; 
}; 

Тогда каждый из производных классов будут вынуждены изменить эти функции, которые должны создавать новые объекты с различными конструкторами.

+0

Я знал, что у нас не может быть виртуального конструктора, и я не хочу его иметь, моя цель - проверить статический код компилятора, который предупредит меня, если я забуду реализовать определенный прототип конструктора. Мой проект представляет собой плагин, такой как динамическая система загрузки, и я каким-то образом применяю реализацию прототипов ctors третьего кода. – chedi

+2

@chedi: Я не понимаю, чего вы пытаетесь достичь здесь. Почему конструкторам нужны эти три? Кажется более разумным (обычным идиоматическим) предоставление фабрики в плагинах, которые принимают те же самые аргументы (что близко к тому, что опубликовал jhoyt). Как вы собираетесь создавать в своем коде экземпляр объекта неизвестного во время компиляции? –

+0

I второй комментарий dribeas. Ваша идея не будет работать, потому что хост-плагин не имеет возможности создавать объекты, которые неизвестны для времени компиляции. Вам нужен завод (например, список функций 'Object * CreateObject (...)', которые должен экспортировать плагин). Вы также облегчите свою жизнь, требуя соглашения о вызове C при экспорте (возвращаемые объекты могут быть C++, конечно), плагины C++ сложны. – Frunsi

0

Если вы забудете реализовать конструктор, но используете его в своем коде, вы получите ошибку компиляции. Например:

Base * p = new Derived(42); 

будет ошибка во время компиляции, если Derived (INT) конструктор не предусмотрено - не будет использоваться базовый конструктор.

1

Я бы, наверное, просто шаблон для генерации теста:

template <typename T> 
void enforceConstructors() { 
    T t1; 
    T t2(0); 
    QString q; 
    T t3(0, q); 
} 

потом где-то в ваших тестах, сделайте следующее:

enforceConstructors<X>(); 
enforceConstructors<Y>(); 
enforceConstructors<Z>(); 

Это может быть все вместе, или в отдельных местах для каждый из классов X, Y, Z. Зависит от того, как вы хотите организовать свои тесты.

Если значения, которые я использовал, не подходят, введите либо некоторые значения, либо компилируйте этот тест, но не запускайте его. Если у вас нет модульных тестов или получить некоторые, либо добавить следующее к классу (вместо унаследованного от базового класса):

#ifndef NDEBUG 
    static void test() { enforceConstructors<X>(); } 
#endif 

Вы обычно не нужно, чтобы конструкторы часть интерфейса определяемый абстрактным базовым классом. Причина в том, что такие интерфейсы предназначены для динамического полиморфизма - вы передаете объект какой-то функции, и он вызывает на нем функции. Вы не можете «передать» класс функции и создать экземпляр класса, отличного от шаблонов. Шаблоны в большинстве случаев обеспечивают соблюдение своих интерфейсов во время компиляции - если вы создаете экземпляр шаблона и используете конструктор, тогда должен быть конструктор.

+0

Возможно, вы захотите добавить к вашему примеру, чтобы применить на классах Y и Z, чтобы показать, как расширить то, что вы хотите. Кроме того, я бы предложил сделать * static void test() * в нестатический и более неясный класс, например: * extern void constructorEnforcer() * при маловероятной вероятности того, что компилятор оптимизировал его с момента статического неиспользованного в файле находится мертвый код. – NVRAM

+0

Неважно, компилятор оптимизирует его. Он по-прежнему должен гарантировать, что это законно, потому что это реальная функция. По крайней мере, я уверен, что вызов неопределенной функции из другой функции требует диагностики на C++ (код шаблона является исключением из этого правила, если он не создан и вызываемая функция зависит от параметра шаблона). Здесь шаблон должен быть создан, даже если 'test()' как-то удален как мертвый код в более поздней точке. C++ не позволяет скомпилировать мертвый код, просто позволяет его удалить позже. –

+0

Подумайте об этом, этот код гарантирует, что соответствующие конструкторы будут объявлены *. Если конструкторы объявлены, но не определены, то этот код может не обнаружить эту проблему, если вы никогда не связываете какой-либо код, который фактически выполняет тест. Я не уверен, что мы должны делать - в любом случае лучше всего выяснить некоторые параметры, которые работают, и запустить тесты. –

1

Из этого комментария к одному из ответов я думаю, что вы действительно не хотите достичь того, что вы здесь просите, а совсем другое. Комментарий Я имею в виду это:

Часть 1:

Я знал, что мы не можем иметь виртуальный конструктор, и я не хочу иметь один, моя цель состоит в том, чтобы компилятор статическая проверка кода, которая сообщит мне обо мне, если я забыл реализовать определенный прототип конструктора .

Часть 2:

Мой проект является плагин как динамическая система загрузки и я в некотором роде обеспечить соблюдение ctors прототипы реализации третьего кода партии.

Что вы спрашиваете, в вопросе 1, и вы можете применять его по-разному, только что прочитал некоторые из ответов, или посмотрите на примеры метапрограммирования и подталкивание :: type_traits библиотека.

Теперь, если вам действительно нужна часть 2: предоставить динамический механизм плагинов загрузки, вам не нужно применять конструкторы, а общий интерфейс как для объектов плагина, так и для создания объектов плагина. Невозможно создать экземпляр объекта неизвестного объекта (во время компиляции), а это значит, что вы не сможете вызвать конструкторы из вашего кода. Я бы предложил

// Interface: 
class plugin { 
public: 
    virtual void operation() = 0; 
}; 
class plugin_factory { 
public: 
    virtual plugin* create() = 0; 
    virtual plugin* create(unsigned int) = 0; 
    virtual plugin* create(unsigned int, QString const &) = 0; 
}; 

Пользователи должны будут обеспечить реализацию плагина и фабрики, которая их создает.Им, вероятно, понадобится внедрить точку входа для своей библиотеки, чтобы вы могли получить доступ к фабрике (или чтобы они могли зарегистрировать свой завод в вашей системе, или я бы предложил использовать библиотеку для этих целей (boost :: extension похоже на место, чтобы смотреть на)

0

я, наконец, принял это решение, но не бросить convienced:


#ifdef NDEBUG 

#ifndef ENFORCE_CTORS 
#define ENFORCE_CTORS(enforcingTemplate, enforcedClass) \ 
    friend void enforcingCtors(){static enforcingTemplate<enforcedClass> _enforcer;} 
#endif 


template<class T> 
     class AbstractEnforcer : T{ 
     public: 
      explicit AbstractEnforcer(){ 
      T enforcedCtor0(         ); 
      T enforcedCtor1(*(new unsigned int)    ); 
      T enforcedCtor2(*(new unsigned int), *(new QString)); 
      T enforcedCtor3(*(new unsigned int), *(new float )); 
      } 
     }; 
#endif 

и в каждом классе, который я wan't для обеспечения я просто добавить, как это:



class X{ 
    ENFORCE_CTORS(AbstractEnforcer, X); 
    /* 
    ..... 
    */ 
} 

Я не нашел другого способа введения этой трески e динамически в классе. И я мог не понимать окончательную цель операции (извините за мой страшный английский).

+0

Я просто сохраняю нотацию стиля параметров * (new typename) для явного типа аргумента, потому что на самом деле выполнение кода выполняется только на этапе отладки проекта, а код просто игнорируется в цикле выпуска. – chedi

Смежные вопросы