C++ 为什么纯虚拟方法不能从不同的继承分支重写?

C++ 为什么纯虚拟方法不能从不同的继承分支重写?,c++,c++11,virtual-functions,linearization,C++,C++11,Virtual Functions,Linearization,我有一个可能有点复杂的类层次结构: class BS { public: virtual void meth()=0; }; class BCA : public virtual BS { }; class BSS : public virtual BS { }; class BCS : public virtual BCA, public virtual BSS { }; class BI4 { public: void meth() {}; }; class

我有一个可能有点复杂的类层次结构:

class BS {
  public:
    virtual void meth()=0;
};

class BCA : public virtual BS {
};

class BSS : public virtual BS {
};

class BCS : public virtual BCA, public virtual BSS {
};

class BI4 {
  public:
    void meth() {};
};

class BT4 : public virtual BI4, public virtual BSS {
};

class T4 : public virtual BCS, public virtual BT4 {
};

int main() {
  T4 t4;
};
现在的问题是,尽管继承图中提供了
void meth()
,但该代码不会编译:

$ g++ -c t.cc -std=c++11
t.cc: In function ‘int main()’:
t.cc:27:6: error: cannot declare variable ‘t4’ to be of abstract type ‘T4’
   T4 t4;
      ^
t.cc:23:7: note:   because the following virtual functions are pure within ‘T4’:
 class T4 : public virtual BCS, public virtual BT4 {
       ^
t.cc:3:18: note:        virtual void BS::meth()
     virtual void meth()=0;
                  ^
t.cc:3:18: note:        virtual void BS::meth()
在我看来,似乎
BS
不知何故不会通过BS->BCA->BCS->T4->BT4->BI4链看到重载的
meth()
方法。

但是为什么呢?该方法是明确可用的,C++使用的C3线性化算法应该能够很清楚地找到它。

<代码> BI4不直接或间接继承< <代码> BS>代码>,所以其方法<代码> BI4::MeMe()/代码>完全无关,不能覆盖<代码> BS::MeMe()/<代码>


您只能重写基类中的方法,而不能重写“兄弟”或“叔叔”类中的方法。

BI4
不能直接或间接地继承自
BS
,因此它的方法
BI4::meth()
完全无关,不能重写
BS::meth()


只能重写基类中的方法,而不能重写“兄弟”或“叔叔”类中的方法。

该语言的规则不允许这样做。虚函数只能通过在派生类中声明具有相同名称和参数的函数来重写。由于
BI4
不是从
BS
派生的,
BI4::meth
无法覆盖
BS::meth
。如果一个类(直接或间接地)从
BS
BI4
继承,那么它继承了两个名为
meth
的函数:一个来自
BS
,仍然是抽象的,没有被重写,另一个来自
BI4
语言规则不允许它。虚函数只能通过在派生类中声明具有相同名称和参数的函数来重写。由于
BI4
不是从
BS
派生的,
BI4::meth
无法覆盖
BS::meth
。如果一个类(直接或间接地)从
BS
BI4
继承,那么它继承了两个名为
meth
的函数:一个来自
BS
,仍然是抽象的,没有被重写,另一个来自
BI4
,主要有两个方面:

  • 给定类只能重写其基类中的成员函数。
    由于您的
    BI4
    类没有
    BS
    作为基类,因此它不能覆盖
    BS
    中的任何内容
  • 可以在虚拟基类中定义的纯虚拟函数的实现中继承,就像在Java中一样,但是提供该实现的类本身也必须具有该虚拟基类
例如:

struct Base
{
    virtual void foo() = 0;
};

#ifdef GOOD
    struct Impl_foo: virtual Base
    {
        void foo() override {}
    };
#else
    struct Impl_foo
    {
        virtual void foo() {}
    };
#endif

struct Abstract_derived: virtual Base
{};

struct Derived
    : Abstract_derived
    , Impl_foo      // Java-like implementation inheritance.
                    // In C++ called "by dominance".
{};

auto main()
    -> int
{
    Derived o;
    o.foo();
}

如果不定义
GOOD
宏符号,此代码将无法编译。

有两个主要方面:

  • 给定类只能重写其基类中的成员函数。
    由于您的
    BI4
    类没有
    BS
    作为基类,因此它不能覆盖
    BS
    中的任何内容
  • 可以在虚拟基类中定义的纯虚拟函数的实现中继承,就像在Java中一样,但是提供该实现的类本身也必须具有该虚拟基类
例如:

struct Base
{
    virtual void foo() = 0;
};

#ifdef GOOD
    struct Impl_foo: virtual Base
    {
        void foo() override {}
    };
#else
    struct Impl_foo
    {
        virtual void foo() {}
    };
#endif

struct Abstract_derived: virtual Base
{};

struct Derived
    : Abstract_derived
    , Impl_foo      // Java-like implementation inheritance.
                    // In C++ called "by dominance".
{};

auto main()
    -> int
{
    Derived o;
    o.foo();
}

如果不定义
GOOD
宏符号,此代码将无法编译。

挑剔:您的意思是“重写”而不是“重载”。多重
virtual
继承关系有点过分。你只需要对可能出现两次或更多次的基类子对象使用它。“有点复杂”o.o如果名称在任何方面都合理的话,它就不会那么糟糕了。“BS->BCA->BCS->T4->BT4->BI4链”上升然后下降;这不是它的工作原理!虽然你不能这样做,但你仍然可以委托给BI4(甚至是模板中的t)。挑剔:你的意思是“重写”而不是“重载”。多重
virtual
继承关系有点过分了。你只需要对可能出现两次或更多次的基类子对象使用它。“有点复杂”o.o如果名称在任何方面都合理的话,它就不会那么糟糕了。“BS->BCA->BCS->T4->BT4->BI4链”上升然后下降;这不是它的工作原理!虽然不能这样做,但仍然可以委托给BI4(甚至是模板中的t)。