c++ - 类继承 C++

标签 c++ inheritance compiler-errors multiple-inheritance

我有一个关于类继承的问题。

我认为以下代码将解释我的问题:

struct A {
   int x;
};

struct B: A {
};

struct C: A {
};

struct D: B, C {
    D() : x(1) {}
};

int main() {
    D d;
}

代码无法编译。

问题是:如何创建 struct D 的实例?谢谢!

最佳答案

您正在尝试做的事情有两个问题。

  • D没有名为 x 的成员,它有两个名为 x 的成员.具体来说,D::B::xD::C::x , 由于多重继承和称为 diamond problem 的某个问题.作为BC来自 A ,它们每个都包含 A 的全部内容在他们的定义中,因此每个都包含自己的A::x .所以,当 D源自 BC , 它包含一个完整的 B和整个 C在其自身中,包括他们的A s。它看起来像这样(使用 MSVC 生成,使用编译器开关 -d1reportSingleClassLayout ):
    class D size(8):
        +---
        | +--- (base class B)
        | | +--- (base class A)
     0  | | | x
        | | +---
        | +---
        | +--- (base class C)
        | | +--- (base class A)
     4  | | | x
        | | +---
        | +---
        +---
    

    您应该改为修改 BC从 A 虚拟继承,如:
    struct B: virtual A {
    };
    
    struct C: virtual A {
    };
    

    这将导致 BC到,而不是包含它们的基数 A ,无论他们走到哪里,让它跟随他们:
    class B size(8):
        +---
     0  | {vbptr}
        +---
        +--- (virtual base A)
     4  | x
        +---
    
    class C size(8):
        +---
     0  | {vbptr}
        +---
        +--- (virtual base A)
     4  | x
        +---
    

    这反过来又允许 D拍一张A ,坚持A到它的背上,告诉他们俩这是他们的A ,解决菱形问题并允许他们共享同一个实例。
    class D size(12):
        +---
        | +--- (base class B)
     0  | | {vbptr}
        | +---
        | +--- (base class C)
     4  | | {vbptr}
        | +---
        +---
        +--- (virtual base A)
     8  | x
        +---
    
  • 一个类只能在 member initialiser list 中初始化它自己的成员。 ;不允许初始化其基类的成员。这是因为 C++ 中的对象是分步构造的:
  • 首先,构造继承层次结构中任何地方遇到的任何虚拟基。派生最多的类被认​​为负责它们的构造。 [如果最派生类在其初始化列表中提及其任何虚拟基,则该虚拟基将被传递指定的参数。如果任何直接基类在其初始化列表中提及任何虚拟基类,它将被忽略。]
  • 然后,构造对象的直接基类(如果有)。对象构造规则以递归方式应用,这意味着将首先构造最小派生类(即位于继承层次结构最底部的类)。如果直接基类具有虚拟基类,则此时不会构造该虚拟基类。 [如果派生类在其初始化列表中提及其任何直接基类,则该基类将被传递指定的参数。]
  • 最后,派生类是围绕它的直接基类构建的,然后是它的虚拟基类。此时构造非静态数据成员;如果在初始化列表中,它们将使用指定的参数构造,否则默认构造。

  • 因此,D应该将参数传递给 A的构造函数,所以他们可以初始化字段。
    struct A {
        int x;
    
        // If A() has no parameter specified, it sets x to 4.
        A(int x_ = 4) : x(x_) {}
    };
    
    struct B: virtual A {
        // If B constructs A, it tells it to set x to 3 unless otherwise specified.
        B(int x_ = 3) : A(x_) {}
    };
    
    struct C: virtual A {
        // If C constructs A, it tells it to set x to 2 unless otherwise specified.
        C(int x_ = 2) : A(x_) {}
    };
    
    struct D: B, C {
        // This will actually set D.x to 4; since D constructs A, neither B() nor C() will
        //  call A().  D() will call A() without specifying a parameter.
        // D() : B(1), C(1) {}
    
        // This, however, gets the job done.  Tells A to set x to 1.
        D() : A(1) {}
    };
    

    然后,当你构造 d ...
    int main() {
        D d;
    }
    

    其成员(member)d.x现在将设置为 1 , 如预期。

    请注意,如果出于某种原因您确实想要 D包含 A 的两个实例,因此避免使用虚拟继承,您应该使用构造函数的第一个版本。这样,当 d已构建,d.B::xd.C::x将设置为 1。

    感谢转至curiousguy指出虚拟基地实际上是在直接基地之前构建的,而不是我认为的在它们之后。

    关于c++ - 类继承 C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40181845/

    相关文章:

    ios - CGPoint 编译器错误

    c++ - 了解 visual studio 2010 中的编译器选项差异和严格的 C++ 合规性

    c++ - 声明一个 C++ 集合迭代器

    c++ - nullptr 作为模板参数

    c++ - 虚函数疑点

    c++ - Mixin 构造函数嵌套包扩展

    java - 编译期间 Int 初始化错误

    c++ - 如何初始化对象数组?

    c++ - 在 C++ 中再次运行时,rand() 不会更改值

    c++ - 具有继承的 friend 类