“如果派生类没有虚拟继承基类,则必须定义所有虚拟方法”。如何以正确的方式理解这一点?

"If the deriving class does not inherit the base class virtually, then all virtual methods must be defined".How to understand that in the right way?

提问人:John 提问时间:6/14/2022 更新时间:6/14/2022 访问量:382

问:

根据 wiki,它说[强调我的]: 请注意,配额中的代码片段如下所示

假设在基类中定义了一个纯虚方法。 如果 派生类以虚拟方式继承基类,然后继承纯类 不需要在该派生类中定义虚拟方法。 但是,如果派生类不继承基类 虚拟,则必须定义所有虚拟方法。下面的代码 可以在这里进行互动探讨。

#include <string>
#include <iostream>

class A                     { 
    protected: 
        std::string _msg; 
    public:
        A(std::string x): _msg(x) {} 
        void test(){ std::cout<<"hello from A: "<<_msg <<"\n"; } 
        virtual void pure_virtual_test() = 0; }; 

// since B,C inherit A virtually, the pure virtual method >pure_virtual_test doesn't need to be defined 
class B: virtual public A
{ public: B(std::string x):A("b"){}  };  
class C: virtual public A   {
public: C(std::string x):A("c"){}  }; 

// since B,C inherit A virtually, A must be constructed in each child
// however, since D does not inherit B,C virtually, the pure virtual method in A *must be defined* 
class D: public B,C { 
    public: 
        D(std::string x):A("d_a"),B("d_b"),C("d_c"){}
        void pure_virtual_test() override { std::cout<<"pure virtual hello from: "<<_msg <<"\n"; } }; 

// it is not necessary to redefine the pure virtual method after the
parent defines it class E: public D { 
    public: 
    E(std::string x):A("e_a"),D("e_d"){}   };


int main(int argc, char ** argv){
    D d("d");
    d.test(); // hello from A: d_a
    d.pure_virtual_test(); // pure virtual hello from: d_a

    E e("e"); 
    e.test(); // hello from A: e_a
    e.pure_virtual_test(); // pure virtual hello from: e_a 
}

如何以正确的方式理解粗体语句?

似乎如果派生类(即 )没有虚拟继承基类,那么虚拟方法可以不定义。这是我的演示代码片段,以支持我所说的:class B

#include <string>
#include <iostream>

class A                     { 
    protected: 
        std::string _msg; 
    public:
        A(std::string x): _msg(x) {} 
        void test(){ std::cout<<"hello from A: "<<_msg <<"\n"; } 
        virtual void pure_virtual_test() = 0;
}; 

// Attention: B does not inherit A ***virtually***, the pure virtual method pure_virtual_test doesn't need to be defined, either.
class B:  public A   { public: B(std::string x):A("b"){}  }; 


class D: public B { 
    public: 
        D(std::string x):B("d_b"){}
        void pure_virtual_test() override { std::cout<<"pure virtual hello from: "<<_msg <<"\n"; }
}; 

// it is not necessary to redefine the pure virtual method after the parent defines it
class E: public D { 
    public: 
    E(std::string x):D("e_d"){}  
}; 

int main(int argc, char ** argv){
    D d("d"); 
    d.test(); 
    d.pure_virtual_test();

    E e("e"); 
    e.test(); 
    e.pure_virtual_test();
}
C++ C++11 继承抽象 纯虚拟

评论

1赞 Remy Lebeau 6/14/2022
任何需要实例化的类都必须实现所有纯虚方法,无论是直接在该类中还是从基类继承。任何未实现纯虚方法的类都无法实例化。
0赞 John 6/14/2022
@RemyLebeau 感谢您的回复。我理解这个规则。让我感到困惑的是 wiki 中的声明。根据我的测试,似乎即使派生类(即 )没有虚拟继承基类,也可以不定义虚拟方法。class B
1赞 Peter 6/14/2022
wiki 中的陈述是错误的。重写继承的纯虚函数的需要是相同的,无论基类是否为虚类。如果派生类不需要实例化,则不需要重写继承的虚函数。无论基础是否虚拟继承,该规则都是一样的。

答:

3赞 Sam Varshavchik 6/14/2022 #1

维基百科文章中的描述是错误的/误导性的。

“如果派生类不以虚拟方式继承基类,则必须定义所有虚拟方法”仅在派生类被实例化时才为 true。没有实例化的声明不需要定义纯虚拟方法。

维基百科文章声称“由于 D 不以虚拟方式继承 B,C,因此必须定义 A 中的纯虚方法”是根本不正确的,下面的编译没有任何问题,没有实例化纯虚方法:DE

#include <string>
#include <iostream>

class A                     {
    protected:
        std::string _msg;
    public:
        A(std::string x): _msg(x) {}
        void test(){ std::cout<<"hello from A: "<<_msg <<"\n"; }
        virtual void pure_virtual_test() = 0;
};

// since B,C inherit A virtually, the pure virtual method pure_virtual_test doesn't need to be defined
class B: virtual public A   { public: B(std::string x):A("b"){}  };
class C: virtual public A   { public: C(std::string x):A("c"){}  };

class D: public B,C {
    public:
        D(std::string x):A("d_a"),B("d_b"),C("d_c"){}
};

class E: public D {
    public:
    E(std::string x):A("e_a"),D("e_d"){}
};

int main()
{
    return 0;
}

main留空,并声明没有问题。现在,如果你试图实例化一个或另一个,那么你就会遇到问题。DE

评论

0赞 John 6/14/2022
我同意你的看法。我们认为几乎是一样的。:)
1赞 Pete Becker 6/14/2022 #2

引用的文本似乎指的是一种称为支配的规则,这导致了一种情况,即它看起来就像一个虚拟函数没有被覆盖,即使它被覆盖了。下面是一个示例:

struct base {
    virtual void f();
};

struct i1 : base {
    // doesn't override base::f
};

struct i2 : base {
    void f() {} // overrides base::f

struct derived : i1, i2 {
};

使用此层次结构,您可以编写

derived d;
d.f(); // illegal, because ambiguous:
       // i1::f, inherited from base, or i2::f
i1* p = &d;
p->f(); // calls base::f

很简单,对吧?现在让我们从 virtual 进行继承。这就是主导地位发挥作用的地方:base

struct base {
    virtual void f();
};

struct i1 : virtual base {
    // doesn't override base::f
};

struct i2 : virtual base {
    void f() {} // overrides base::f

struct derived : i1, i2 {
};

现在只有一个子对象,并且覆盖 ,即使在 :basei2::fbase::fi1

derived d;
d.f();    // calls i2::f
i1* i1p = &d;
i1p->f(); // calls i2::f

是的,调用调用的 的版本,即使对 一无所知。只是因为 和 都是 的基类,并且都具有虚拟基,这才起作用。编译器必须为这种跨层次结构调用获取正确的代码。i1p->f()i2fi1i2i1i2derivedbase

制作纯粹的虚拟并不会改变寻找其超凡者的规则。所以对于非虚拟继承,不会覆盖,并且是一个抽象类。但是对于虚拟继承,覆盖,并且类不是抽象的。base::fi1base::fderivedi2::fbase::fderived