3.4 钻石继承

1> 钻石继承问题
– 一个子类继承自多个基类,而这些基类又源自共同的祖先,这样的继承结构称为钻石继承(菱形继承)
– 公共基类子对象,在汇聚子类对象中,存在多个实例
– 在汇聚子类内部,或通过汇聚子类对象,访问公共基类的成员,会因继承路径的不同而导致匹配歧义


// 虚继承 -- 钻石继承先天有缺陷的解决方法
// (1) 公共基类(A类)子对象,在汇聚子类(Z类)对象中只存在一份
// (2) 公共基类(A类)子对象,要被多个中间子类(X/Y)子对象共享

#include <iostream>
using namespace std;
class A { // 公共基类 (人类)
public:
int m_a; // 年龄
};
class X : public A { // 中间子类 (老师类)
public:
int m_x;
};
class Y : public A { // 中间子类 (学生类)
public:
int m_y;
};
class Z : public X, public Y { // 汇聚子类 (助教类)
public:
int m_z;
int m_z1;
void foo() {
//m_a = 28; // 歧义
X::m_a = 28; // 不是解决问题的方法
}
};
// 以上代码模拟类的设计者(c++标准类/第三方类/自己设计的类...)
// -----------------------------------------------------------
// 以下代码模拟类的使用者(用户)
int main( void ) {
Z z; // |X中间子类子对象|Y中间子类子对象|m_z|-->|A公共基类子对象 m_x|A公共基类子对象 m_y|m_z|
// -->|m_a m_x|m_a m_y|m_z|
cout << "汇聚子类对象z的大小: " << sizeof(z) << endl; // 20

//z.m_a = 30; // 歧义
z.Y::m_a = 30; // 不是解决问题的方法
return 0;
}

执行结果

day07$./a.out
汇聚子类对象z的大小: 24

注:z对象中有两个m_a,虽然在类内或类外通过访问控制限定符可以指定 m_a, 但并不合理。

2> 虚继承
• 虚继承是钻石继承问题解决方法
– 在继承表中使用virtual关键字
– 虚继承可以保证
(1) 公共虚基类子对象在汇聚子类对象中仅存一份实例
(2) 公共虚基类子对象被多个中间子类子对象所共享

 • 虚继承实现原理
– 汇聚子类对象中的每个中间子类子对象都持有一个指针,通过该指针可以获取 中间子类子对象的首地址 到 公共虚基类子对象的首地址的 偏移量

// 虚继承  -- 钻石继承先天有缺陷的解决方法
// (1) 公共基类(A类)子对象,在汇聚子类(Z类)对象中只存在一份
// (2) 公共基类(A类)子对象,要被多个中间子类(X/Y)子对象共享
#include <iostream>
using namespace std;
#pragma pack(1)
class A { // 公共基类 (人类)
public:
    int m_a; // 年龄
};
class X : virtual public A { // 中间子类 (老师类)
public:
    int m_x;
    void setAge( /* X* this */ int age ) {
        this->m_a = age; // this->X中间子类子对象->指针1->偏移量-->this+偏移量-->A公共基类子对象-->m_a
    }
};
class Y : virtual public A { // 中间子类 (学生类)
public:
    int m_y;
    int getAge( /* Y* this */ ) {
        return this->m_a; // this->Y中间子类子对象->指针2->偏移量->this+偏移量->A公共基类子对象-->m_a
    }
};
class Z : public X, public Y { // 汇聚子类 (助教类)
public:
    int m_z;
    void foo() {
        m_a = 28; 
    }
};
// 以上代码模拟类的设计者(c++标准类/第三方类/自己设计的类...)
// -----------------------------------------------------------
// 以下代码模拟类的使用者(用户)
int main( void ) {
    Z z; // |X中间子类子对象|Y中间子类子对象|m_z|A公共基类子对象|-->|指针1 m_x|指针2 m_y|m_z|m_a|
    cout << "汇聚子类对象z的大小: " << sizeof(z) << endl; // 32
    z.m_a = 28; 
    cout << z.getAge() << endl; // getAge(&z)-->实参Z*

    z.setAge( 18 ); // setAge(&z,18)-->第一个实参Z*
    cout << z.getAge() << endl; // getAge(&z)-->实参Z*

X x;
Y y;
cout << "对象x的大小: " << sizeof(x) << endl; // 16
cout << "对象y的大小: " << sizeof(y) << endl; // 16


return 0; }

执行结果
$./a.out
汇聚子类对象z的大小: 32
28
18
对象x的大小: 16
对象y的大小: 16

 

posted @ 2025-05-07 20:38  靖意风  Views(11)  Comments(0)    收藏  举报