[英]C++ OO design: Inheritance of template parameter
我有一个继承链,其中Base是基类。 我希望能够编写一个继承了Base并可能继承另一个Base派生类的类模板。 我可以使用虚拟继承,但是找到了另一个解决方案。 我想知道它是否是通用/合理/合法的类设计:
编写一个类模板,其中template参数是它派生自的类,即它必须是Base或Base派生的类。 在构造函数中,我可以使用静态断言来确保用户没有使用任何非法类作为模板参数。
如果可行,我将永远不会遇到虚拟继承问题……问题是,这样做是可以的。 我在其他项目中从未见过它,因此我想确定在使用它之前。
编辑 :为了确保我不会混淆您,这是一些代码:
class Base
{
};
class Derived : public Base
{
};
template <Class TheBase>
class MyDerived : public TheBase
{
};
现在,我可以将Base
或任何Base
派生的类(例如Derived
)用作TheBase
参数。
这是有效的设计模式。 它是mixin继承,而不是CRTP。 Mixin继承通过程序员手动线性化继承层次结构,提供了一种安全地模拟多重继承的方法。 模板化的类是mixins。 如果要使用多个mixin扩展类,则必须确定合成的顺序,例如Big<Friendly<Dog> >
。 Dobb博士的文章中介绍了C ++中的Mixin编程。 混入可以被用来实现静态版本GoF的Decorator模式的描述在这里 。 Mixins在C ++中扮演类似的角色,特征(不是C ++特征)在Scala和SmallTalk中扮演。
在CRTP中,基类是模板:
template <class Param>
class Base { ... };
class Derived : public Base<Derived> { ... };
正如Zadirion所指出的,这很好。 它起作用(简化)的原因是,与C#中的泛型不同,C ++中的模板是编译时的。 我会说“这是一个typedef”,这让我很失落,对此我会感到很惊讶,但是让我们保持简单并说出来。
考虑:
class base {
protected:
base() { };
virtual ~base() { };
};
template<class T>
class super : public T {
};
然后:
super<base> s;
绝对好 这实际上是一个相当漂亮的构造。 因为是编译时间,所以您可以选择基类 ,在某些设计惯用法中,这可能是非常有利的。
这是一个很好的座右铭: 对类型使用模板,对行为使用继承。
坚持下去。 当然,您可以使用很多捷径/技巧来完成工作,但从长远来看,这些糟糕的设计选择将令人头疼。 如果要使用这种方法,请务必研究其优缺点。
现在,回到您的问题,您可以做的是:参见CRTP和Static polymorphism 。
听起来您在谈论的是“好奇地重复出现的模板模式” ,它是有效的C ++。
您想做的是从两个可能具有共同基类的类继承,是正确的吗? 在那种情况下,您应该处理虚拟继承问题(即,您必须将您感兴趣的两个类的基类的继承声明为虚拟 )。 由于某些运行时支持(仅2个vpointer),这只会导致很小的开销(可能微不足道)。
您的代码不是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;
}
通过创建基类的两个不同实例,您将避免继承歧义。
如果您打算同时从基类和基类继承,则一个更简单,也许更干净的更好的解决方案如下:
如果您在课堂设计中注意潜在的名称隐藏 问题,而只是使用多态性“自定义”您实际上想要有所不同(并且可以强制转换控制)的功能的行为,则可以使用Base -> Derived -> YourClass
层次结构Base -> Derived -> YourClass
最终可以解决您的问题。
在您的特定情况下,您的方法可以奏效,正如其他人指出的那样,在许多应用程序中都使用过这种方法,但是我认为无法有效解决您的双重继承问题。 最终,只有特定的设计案例才能导致邪恶的解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.