C++ 我们可以在菱形继承模式中只使用一个虚拟继承吗?

C++ 我们可以在菱形继承模式中只使用一个虚拟继承吗?,c++,C++,假设我有一些类:Base,Derived1,Derived2,…等等。所有派生类都派生自Base。现在,我想为所有派生类添加一些功能,但不能修改这些类。因此,我提出了以下方法: 使用虚拟继承创建一个类,如下所示: class Additional: virtual public Base { void f() { //some codes operating on class Base } }; 然后我可以通过以下方式扩展每个派生类: class MyDeriv

假设我有一些类:
Base
Derived1
Derived2
,…等等。所有派生类都派生自
Base
。现在,我想为所有派生类添加一些功能,但不能修改这些类。因此,我提出了以下方法:

使用虚拟继承创建一个类,如下所示:

class Additional: virtual public Base {
    void f() {
        //some codes operating on class Base
    }
};
然后我可以通过以下方式扩展每个派生类:

class MyDerived1: public Derived1, public Additional {};
然而,当我搜索互联网时,似乎在钻石继承的情况下,我们必须对这两个类
Derived1
Additional
使用虚拟继承。因此,上述方法将不起作用。因此,我的问题是:

  • 为什么我们必须在菱形继承案例中使用两个虚拟继承
  • 如果我的方法无效,是否有其他方法可以解决上述困难
多谢各位

为什么我们必须在菱形继承案例中使用两个虚拟继承

虚拟继承是类必须自愿签订的契约。该类基本上表示它愿意与另一个派生类的实例共享其基子对象。它影响对象布局和对类代码中基的访问。您不能追溯更改这些内容,尤其是在类实现已编译且仅作为对象文件提供给您的情况下

如果我的方法无效,是否有其他方法可以解决上述困难

是的,使用模板

template<class BaseT>
class Additional: public BaseT {
    void f() {
        //some codes operating on class Base
    }
};

using MyDerived1 = Additional<Derived1>;
模板
附加类:公共BaseT{
void f(){
//一些在类基上运行的代码
}
};
使用MyDerived1=附加值;
现在,我想为所有派生类添加一些功能

您可以编写自由函数

void some_functionality(Base & base)
{
    ...
}
为什么我们必须在菱形继承案例中使用两个虚拟继承

这里有两个问题。为什么我们需要使用虚拟继承呢

  • 虚拟继承具有运行时成本。假设您正在将指向
    Derived1
    的指针或引用转换为指向
    Base
    的指针或引用。使用普通的固有特性,只需要添加一个在编译时已知的固定数值偏移量(该偏移量通常为零,因此根本没有成本!)。对于虚拟继承,您必须经历某种指针间接寻址。这种转换发生在可能不明显的地方,例如,无论何时调用在基类上定义的方法,都是隐式进行的。大多数类都没有利用这种虚拟继承,所以它们都要为此付出代价是没有意义的
  • 如果您从
    Derived1
    Derived2
    继承,而在
    Base
    上没有虚拟继承,那么您将得到两个类型为
    Base
    的子对象。有时候这就是你想要的!如果C++总是给你虚拟继承,即使你不要求它,那么你将失去这个能力。
另一个问题是:为什么我们在这两个地方都需要“
virtual
”?换句话说,当我们以后创建另一个类时,为什么我们不能在一段代码中重写一个使用非虚拟继承的决定呢

    部分是因为C++中编译的工作原理。如果
    Derived1
    实际上从
    Base
    继承,那么这将影响所有
    Derived1
    对象,甚至是那些不属于
    附加
    的对象。(必须是这样,因为引用
    Derived1
    的函数不知道它是否是
    附加
    的一部分。)您建议您的
    Additional
    的定义回到
    Derived1
    的定义中,并将其含义更改为虚拟继承-即使在不包含头文件的编译单元中
  • 部分原因是,您可能需要两个子对象。假设您有
    D1
    D2
    D3
    ,其中
    D1
    非虚拟继承自
    Base
    ,而
    D2
    D3
    虚拟继承自
    Base
    。然后您将得到两个
    Base
    子对象:一个用于
    D1
    ,另一个在
    D2
    D3
    之间共享。诚然,这是非常不寻常的,但这可能是你想要的,而且你的建议会阻止它

使用模板的解决方案非常棒。我只有一个小小的疑问:在编译后,函数f()是否会在extecucable文件中的多个派生类中多次出现?@zbh2047-可能会。然而,另一方面,如果它足够简单,它可能根本不会出现。模板也倾向于内联。“菱形继承模式”没有一个正式的定义,大多数关于它的文章都是肤浅的。