#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.cppclass 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.cppclass GrandSon size(12):+---0 | +--- (base class Son1)0 | | {vbptr}| +---4 | +--- (base class Son2)4 | | {vbptr}| +---+---+--- (virtual base Base)8 | i+---GrandSon::$vbtable@Son1@:0 | 01 | 8 (GrandSond(Son1+0)Base)GrandSon::$vbtable@Son2@:0 | 01 | 4 (GrandSond(Son2+0)Base)vbi: class offset o.vbptr o.vbte fVtorDispBase 8 0 4 0D:\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\include\ostream(285): warning C4530: 使用了 C++ 异常处理程序,但未启用展开语义。请指定 /EHscD:\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;}
总结:
- 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
 - 利用虚继承可以解决菱形继承问题
 
