симулировать виртуальный конструктор в с ++ - PullRequest
4 голосов
/ 05 декабря 2009

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

Вчера я нашел сумасшедший способ симулировать состав виртуального 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 () никогда не вызывается (и даже при этом он ничего не делает [надеюсь на это !!!])

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


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

}

Ответы [ 6 ]

4 голосов
/ 05 декабря 2009

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

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

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;
}

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

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

Редактировать - Невыпадающий Enforcer (): [Поскольку абсолютно нет необходимости использовать динамическое распределение там ..]

        static void Enforcer(){
            T t1();
            T t2(int(3));
            T t3(int(4), QString());
        }
3 голосов
/ 05 декабря 2009

См. эту страницу в C ++ FAQ.

Я бы сделал что-то вроде этого:

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

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

1 голос
/ 05 декабря 2009

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

Часть 1:

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

Часть 2:

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

То, что вы спрашиваете в этом вопросе, равно 1, и вы можете применить его по-разному, просто прочитав некоторые ответы, или посмотрите на примеры метапрограммирования и библиотеку boost :: 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) похоже на место, чтобы посмотреть)

1 голос
/ 05 декабря 2009

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

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 голосов
/ 07 декабря 2009

Я, наконец, принял это решение, но не окончательно:


#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

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



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

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

0 голосов
/ 05 декабря 2009

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

Base * p = new Derived( 42 );

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...