4

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

}
4

6 回答 6

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());
        }
于 2009-12-05T09:07:52.853 回答
3

请参阅C++ 常见问题解答中的此页面

我会做的是这样的:

类抽象类{
    上市:
        虚拟 AbstractClass* create() const = 0;
        virtual AbstractClass* create(unsigned int) const = 0;
        virtual AbstractClass* create(unsigned int, QString) const = 0;
};

然后每个派生类将被迫重写这些函数,这些函数应该创建具有不同构造函数的新对象。

于 2009-12-05T10:16:50.117 回答
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似乎是一个可以看的地方)

于 2009-12-05T15:54:26.950 回答
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

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

于 2009-12-05T14:34:28.173 回答
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);
  /*
    .....
  */
}

我没有找到任何其他方法在类中动态注入此代码。而且我可能不清楚这次行动的最终目的(对不起我糟糕的英语)。

于 2009-12-07T16:30:35.697 回答
0

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

Base * p = new Derived( 42 );

如果未提供 Derived(int) 构造函数,将是编译时错误 - 将不使用 Base 构造函数。

于 2009-12-05T10:34:02.090 回答