多重继承+虚函数混乱

sho*_*osh 17 c++ virtual-functions multiple-inheritance diamond-problem

我有一个像这样的钻石多重继承场景:

    A
  /   \
 B     C
  \   /
    D
Run Code Online (Sandbox Code Playgroud)

公共父A定义了虚函数fn().
B和C都可以定义fn()吗?
如果是,那么下一个问题是 - D可以访问B和C的fn()而不消除歧义吗?我假设有一些语法...
而D是否有可能在不知道谁是B和C的情况下做到这一点?B和C可以替换为其他类,我希望D中的代码是通用的.

我想要做的是让D以某种方式枚举它在其祖先中具有的fn()的所有实例.这是否可能在某些其他方面表示虚函数?

Joh*_*itb 19

除非你fn再次覆盖D,否则不可能.因为D对象中没有最终覆盖:Both CB覆盖A::fn.你有几个选择:

  • 掉落C::fnB::fn.然后,仍然覆盖的那个A::fn具有最终的覆盖.
  • 放在D.最终置换器之后,一个覆盖A::fn藏汉为fnCB.

例如,以下结果导致编译时错误:

#include <iostream>

class A {
public:
    virtual void fn() { }
};

class B : public virtual A {
public:
    virtual void fn() { }
};

class C : public virtual A {
public:
    virtual void fn() { }
};

// does not override fn!!
class D : public B, public C {
public:
    virtual void doit() {
        B::fn();
        C::fn();
    }
};

int main(int argc, char **argv) {
  D d;
  d.doit();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,您可以从C和B中的A派生非虚拟,但之后您不再有钻石继承.也就是说,A中的每个数据成员在B和C中出现两次,因为在D对象中有两个A基类子对象.我建议你重新考虑一下这个设计.尝试消除需要虚拟继承的双重对象.它经常导致这种冲突的情况.

与此非常类似的情况是您想要覆盖特定功能.想象一下,你有一个在B和C中具有相同名称的虚函数(现在没有公共基数A).在D中,您希望覆盖每个函数,但为每个函数赋予不同的行为.根据您是使用B指针还是C指针调用该函数,您具有不同的行为.Herb Sutter的多重继承第三部分描述了一种很好的方法.它可能会帮助您决定您的设计.

  • 这就是我说不可能的原因.如果C和B都覆盖A :: fn,那么他不能在不重写D中的fn的情况下定义D. (3认同)

viv*_*dos 5

第一个问题,是的,B和C可以定义fn()为虚函数。其次,D 当然可以B::fn()通过C::fn()使用作用域运算符 :: 第三个问题:D 必须至少知道 B 和 C,因为您必须在继承列表中定义它们。可以使用模板让B和C的类型开放:

class A
{
public:
   virtual ~A() {}
   virtual void fn() = 0;
};

class B: public A
{
public:
   virtual ~B() {}
   virtual void fn(){ std::cout << "B::fn()" << std::endl; }
};

class C: public A
{
public:
   virtual ~C() {}
   virtual void fn(){ std::cout << "C::fn()" << std::endl; }
};

template <typename TypeB, typename TypeC>
class D: public TypeB, public TypeC
{
public:
   void Do()
   {
      static_cast<TypeB*>(this)->fn();
      static_cast<TypeC*>(this)->fn();
   }
};

typedef D<B, C> DInst;

DInst d;
d.Do();
Run Code Online (Sandbox Code Playgroud)

关于自动枚举 D 继承自的所有类的所有 fn() 函数的愿望:我不确定在不诉诸 MPL 的情况下这是否可能。至少你可以用处理 3 个或更多模板参数的版本来扩展我上面的示例,但我猜类模板参数的数量有一个(内部编译器)上限。

  • 想象你有这样的代码: A *a = some_d_object; a-&gt;fn(); 将调用什么版本的 fn? (2认同)
  • @j_random_hacker“_我错了_”你假设`D`中有**一个**基类`A`(尝试`A&amp; = d;`),所以有**一个**成员函数`A::fn()` (尝试 `dA::fn();`)。有两个基址“A”(对 c|dtor 进行检测以查看其地址),因此“d”中有两个成员“A::fn()”:“dB::A::fn()”和“dC”: :A::fn()`。他们每个人确实都有一个最终的覆盖者。 (2认同)

Sco*_*e T -1

已经有几个问题涉及这个问题。看来我们已经没有什么问题要问了。也许搜索框应该比提问按钮大。