一个类继承c++

A class inheritance C++

本文关键字:继承 c++ 一个      更新时间:2023-10-16

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

我想下面的代码可以解释我的问题:

struct A {
   int x;
};
struct B: A {
};
struct C: A {
};
struct D: B, C {
    D() : x(1) {}
};
int main() {
    D d;
}

代码无法编译。

问题是:如何创建结构体D的实例?谢谢!

你要做的有两个问题。

  1. D没有一个成员x,它有两个成员x。具体来说,D::B::xD::C::x由于多重继承而存在一定的问题,称为菱形问题。由于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
        +---
    
  2. 类只能在成员初始化器列表中初始化自己的成员;不允许初始化其基类的成员。这是因为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;
    }
    

    它的成员d.x现在将被设置为1

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


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