繁体   English   中英

C ++ OO设计:模板参数的继承

[英]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> { ... };

以后编辑:一年后,我在这里修改自己的答案。 我最初错误地指出发布的模式OP是CRTP。 这是不正确的。 确实是一个问题,请阅读本页下方Daniel Mahler的答案以获取正确的解释。

原文:可以使用这样的设计。 WTL例如使用它。 它用于实现静态多态性 ,称为好奇重复模板模式

正如Zadirion所指出的,这很好。 它起作用(简化)的原因是,与C#中的泛型不同,C ++中的模板是编译时的。 我会说“这是一个typedef”,这让我很失落,对此我会感到很惊讶,但是让我们保持简单并说出来。

考虑:

class base {
protected:
    base() { };
    virtual ~base() { };
};

template<class T>
class super : public T {
};

然后:

super<base> s;

绝对好 这实际上是一个相当漂亮的构造。 因为是编译时间,所以您可以选择基类 ,在某些设计惯用法中,这可能是非常有利的。

这是一个很好的座右铭: 对类型使用模板,对行为使用继承。

坚持下去。 当然,您可以使用很多捷径/技巧来完成工作,但从长远来看,这些糟糕的设计选择将令人头疼。 如果要使用这种方法,请务必研究其优缺点。

现在,回到您的问题,您可以做的是:参见CRTPStatic 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 继承自Base的类继承使我认为您希望能够同时访问Base方法和Base派生的方法。

如果您在课堂设计中注意潜在的名称隐藏 问题,而只是使用多态性“自定义”您实际上想要有所不同(并且可以强制转换控制)的功能的行为,则可以使用Base -> Derived -> YourClass层次结构Base -> Derived -> YourClass最终可以解决您的问题。

在您的特定情况下,您的方法可以奏效,正如其他人指出的那样,在许多应用程序中都使用过这种方法,但是我认为无法有效解决您的双重继承问题。 最终,只有特定的设计案例才能导致邪恶的解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM