我有一个继承链,Base是基类。我希望能够编写一个继承Base和另一个Base派生类的类模板。我可以使用虚拟继承,但我发现了另一种解决方案。我想知道它是否是普通/相当/合法的课程设计:
编写一个类模板,其中模板参数是从中派生的类,即它必须是Base或Base派生类。在构造函数中,我可以使用静态断言来确保用户不使用任何非法类作为模板参数。
如果它有效,我将永远不会有虚拟继承问题......问题是,它可以做到这一点。我从未在其他项目中看到它,所以在使用之前我想确定它。
编辑:为了确保我不会混淆你,这里有一些代码:
class Base
{
};
class Derived : public Base
{
};
template <Class TheBase>
class MyDerived : public TheBase
{
};
现在我可以使用Base
或任何Base
派生类,例如Derived
,TheBase
参数。
答案 0 :(得分:21)
这是一种有效的设计模式。它是mixin继承,而不是CRTP。 Mixin继承提供了一种方法来由程序员手动线性化继承层次结构来安全地模拟多重继承。模板类是mixins。如果要扩展具有多个mixin的类,则必须确定组合的顺序,如Big<Friendly<Dog> >
。
在Dr Dobb's article中描述了C ++中的Mixin编程。
Mixins可用于实现GoF Decorator模式的静态版本,如here所述。
Mixins在C ++中起着类似的作用,Scala&amp; S中的特征(不是C ++特征)起作用。 Smalltalk的。
在CRTP中,它是作为模板的基类:
template <class Param>
class Base { ... };
class Derived : public Base<Derived> { ... };
答案 1 :(得分:10)
后来编辑:一年后,我在这里修改自己的答案。我最初错误地说OP发布的模式是CRTP。这是不正确的。这确实是一个混音,请阅读Daniel Mahler在页面下方的答案,以获得正确的解释。
原文:可以使用这样的设计。例如WTL使用它。它用于实现Static Polymorphism,称为Curiously recurring template pattern
答案 2 :(得分:8)
这很好,正如扎迪里昂指出的那样。它的工作原理(简化)是C ++中的模板,与C#中的泛型不同,是编译时的。说“这是一个类型的定义”对我来说是错误的,而且我会为它做出很多抨击,但让我们保持简单并说它是。
考虑:
class base {
protected:
base() { };
virtual ~base() { };
};
template<class T>
class super : public T {
};
以后:
super<base> s;
绝对没问题。这实际上是一个相当漂亮的结构。因为它是编译时,你可以选择你的基类,这在一些设计习语中可能非常有利。
答案 3 :(得分:5)
这是一个很好的座右铭:对类型使用模板,但对行为使用继承。
坚持下去。肯定会有很多捷径/技巧可以用来完成工作,但从长远来看,这些糟糕的设计选择将令人头疼。如果您想使用此类产品,请务必研究其优缺点。
现在,回到您的问题,您提出的问题是:CRTP和Static polymorphism。
答案 4 :(得分:4)
听起来你在谈论Curiously Recurring Template Pattern,这是有效的C ++。
答案 5 :(得分:1)
你要做的是继承两个可能有共同基类的类,这是正确的吗?在这种情况下,你应该处理虚拟继承问题(即你必须将虚拟声明为你感兴趣的两个类的基类的继承)。由于一些运行时支持(更多2个vpointers),这只会导致一个小的(可能是微不足道的)开销。
你的代码不是CRTP(在CRTP中,基类是模板接收派生类的那个),并且似乎没有以任何方式解决你试图摆脱的双重继承问题。
据我所见,您可以接受虚拟继承,并使用虚拟关键字以最小的开销产生,或者您可以重构代码。
我并不完全理解你要做的是什么,但如果你试图从一个公共基类继承两个不同的类(虚拟继承就是这个),并且由于某种原因你不想要要使用虚拟关键字,您可以按以下方式使用CRTP:
#include <iostream>
using namespace std;
template<class Derived>
class Base
{
public:
void basefunc() { cout << "base here"<< endl; }
virtual void polyfunc() { cout << "base poly here"<< endl; }
};
class Derived : public Base<Derived>
{
public:
void derivedfunc() { cout << "derived here"<< endl; }
virtual void polyfunc() { cout << "derived poly here"<< endl; }
};
class OtherDerived : public Base<OtherDerived>
{
public:
void otherderivedfunc() { cout << "otherderived here"<< endl; }
virtual void polyfunc() { cout << "otherderived poly here"<< endl; }
};
class InheritingFromBoth : public Derived, public OtherDerived
{
public:
void inheritingfunc() { cout << "inheritingfromboth here" << endl; }
virtual void polyfunc() { cout << "inheritingfromboth poly here"<< endl; }
};
int main() {
Derived obj;
OtherDerived obj2;
InheritingFromBoth *obj3 = new InheritingFromBoth();
Derived *der = dynamic_cast<Derived*>(obj3);
der->polyfunc();
OtherDerived *der2 = dynamic_cast<OtherDerived*>(obj3);
der2->polyfunc();
Base<Derived>* bptr = dynamic_cast<Base<Derived>*>(obj3);
bptr->polyfunc();
Base<OtherDerived>* bptr2 = dynamic_cast<Base<OtherDerived>*>(obj3);
bptr2->polyfunc();
return 0;
}
通过创建基类的两个不同实例,您将避免继承歧义。
如果您打算同时从基类和基类派生类继承,那么更简单,也许更干净,更好的解决方案如下:
如果您在课堂上注意潜在的名称隐藏 problems并且只是使用多态来“自定义”您实际想要的功能的行为(那可以被铸造控制),然后一个干净的层次结构Base -> Derived -> YourClass
最终可以解决你的问题。
在您的特定情况下,您的方法可行,正如其他人所指出的那样,在许多应用程序中都使用过,但我认为不能有效地解决您的双重继承问题。最终只有特定的设计案例才能导致 the less-evil 解决方案。