#include <iostream>
#include <string>
using namespace std;
class Base {
public:
int i;
};
class Son1 :public Base {};
class Son2 :public Base {};
class GrandSon : public Son1, public Son2 {};
void example() {
GrandSon s1;
cout << s1.i << endl; // 由于菱形继承,这里会报错
}
int main() {
example();
return 0;
}
当然我们可以使用作用域的方法访问。因为这里实际上对象模型是包含了两个类的
D:\C++\LearnCpp>cl /d1 reportSingleClassLayoutGrandSon LearnCpp.cpp
用于 x86 的 Microsoft (R) C/C++ 优化编译器 19.27.29112 版
版权所有(C) Microsoft Corporation。保留所有权利。
LearnCpp.cpp
class GrandSon size(8):
+---
0 | +--- (base class Son1)
0 | | +--- (base class Base)
0 | | | i
| | +---
| +---
4 | +--- (base class Son2)
4 | | +--- (base class Base)
4 | | | i
| | +---
| +---
+---
LearnCpp.cpp(13): error C2385: 对“i”的访问不明确
LearnCpp.cpp(13): note: 可能是“i”(位于基“Base”中)
LearnCpp.cpp(13): note: 也可能是“i”(位于基“Base”中)
但是可以使用虚继承
继承前加virtual关键字后,变为虚继承 //此时公共的父类Animal称为虚基类
class Base {
public:
int i = 0;
};
class Son1 :virtual public Base {};
class Son2 :virtual public Base {};
class GrandSon : public Son1, public Son2 {};
void example() {
GrandSon s1;
cout << s1.i << endl;
}
int main() {
example();
return 0;
}
此时会正常输出零
D:\C++\LearnCpp>cl /d1 reportSingleClassLayoutGrandSon LearnCpp.cpp
用于 x86 的 Microsoft (R) C/C++ 优化编译器 19.27.29112 版
版权所有(C) Microsoft Corporation。保留所有权利。
LearnCpp.cpp
class GrandSon size(12):
+---
0 | +--- (base class Son1)
0 | | {vbptr}
| +---
4 | +--- (base class Son2)
4 | | {vbptr}
| +---
+---
+--- (virtual base Base)
8 | i
+---
GrandSon::$vbtable@Son1@:
0 | 0
1 | 8 (GrandSond(Son1+0)Base)
GrandSon::$vbtable@Son2@:
0 | 0
1 | 4 (GrandSond(Son2+0)Base)
vbi: class offset o.vbptr o.vbte fVtorDisp
Base 8 0 4 0
D:\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\include\ostream(285): warning C4530: 使用了 C++ 异常处理程序,但未启用展开语义。请指定 /EHsc
D:\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\include\ostream(270): note: 在编译 类 模板 成员函数“std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(int)”时
LearnCpp.cpp(13): note: 查看对正在编译的函数 模板 实例化“std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(int)”的引用
LearnCpp.cpp(13): note: 查看对正在编译的 类 模板 实例化“std::basic_ostream<char,std::char_traits<char>>”的引用
Microsoft (R) Incremental Linker Version 14.27.29112.0
而它的对象模型也会变成一个虚拟指针
vbptr - Virtual Base Pointer这个指针指向了一个vbtable
vbtable - Virtual Base Table 这个值记录了一个偏移量,通过偏移量来找到唯一的值。
这个值是对象的,而且只有一个,是公用的,思想类似于静态的成员变量。
一个实例
菱形继承概念:
两个派生类继承同一个基类
又有某个类同时继承者两个派生类
这种继承被称为菱形继承,或者钻石继承
典型的菱形继承案例:
菱形继承问题:
羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
- 草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。
示例:
class Animal
{
public:
int m_Age;
};
//继承前加virtual关键字后,变为虚继承
//此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal {};
class Tuo : virtual public Animal {};
class SheepTuo : public Sheep, public Tuo {};
void test01()
{
SheepTuo st;
st.Sheep::m_Age = 100;
st.Tuo::m_Age = 200;
cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl;
cout << "st.m_Age = " << st.m_Age << endl;
}
int main() {
test01();
system("pause");
return 0;
}
总结:
- 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
- 利用虚继承可以解决菱形继承问题