在c ++中模拟虚拟构造函数

时间:2009-12-05 08:54:44

标签: c++ stl constructor virtual derived

在我的应用程序中,我必须从基类派生一些类,问题是我想强制派生类具有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 个答案:

答案 0 :(得分:4)

您的解决方案无法解决问题,因为未使用的模板化代码未实例化,因此除非您手动调用此函数,否则它不会验证是否存在所需的构造函数。

您可以做的是从执行者的构造函数调用此方法:

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

答案 1 :(得分:3)

请参阅C ++常见问题解答中的this page

我会做的是这样的事情:

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

然后,每个派生类都将被强制覆盖这些函数,这些函数应该创建具有不同构造函数的新对象。

答案 2 :(得分: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

通常不需要将构造函数作为抽象基类定义的接口的一部分。原因是这样的接口用于动态多态 - 您将对象传递给某个函数,并在其上调用函数。你不能将一个类“传递”给一个函数,并让它实例化除了模板之外的类。模板大多数在编译时强制执行它们的接口 - 如果你实例化模板并使用构造函数,那么构造函数必须在那里。

答案 3 :(得分:1)

从这个评论到其中一个答案,我认为你并不是真的希望在这里实现你所要求的,而是另一回事。我所指的评论是:

第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)好像是一个值得关注的地方)

答案 4 :(得分:0)

如果您忘记实现构造函数但在代码中使用它,则会出现编译错误。例如:

Base * p = new Derived( 42 );
如果未提供Derived(int)构造函数,

将是编译时错误 - 将不使用Base构造函数。

答案 5 :(得分:0)

我终于采用了这个解决方案,但没有放弃使用:


#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);
  /*
    .....
  */
}

我没有找到任何其他方法在类中动态注入此代码。我可能不清楚手术的最终目的(抱歉我可怕的英语)。