最近在看《深度探索C++对象模型》这本书的时候,里面第一章提到了虚拟继承,有这么一句话说:“在虚拟继承的情况下,base class不管在继承串链中被派生多少次,永远只会存在一个实体。”一开始我理解错了,以为这个继承类图体系里面全局只有一个base class对象,后来查了些资料才知道意思是一个子类中只包含一个bass class对象。
很多东西要有对比才能认识得更深刻,我们这里对比下虚拟继承和普通继承的子类对象的区别,也从而让我们更好地理解文章开头提到的书里的那句话。
虚拟继承样例:
class CTestBase { public: int base_data_; }; class CTestSubject1:virtual class CTestBase { public: int subone_data_; }; class CTestSubject2:virtual class CTestBase { public: int subtwo_data_; }; class CTestDiamond:public CTestSubject1,public CTestSubject2 { public: int dia_data_; }; int main() { CTestDiamond dia; dia.base_data_ = 1; }
上面当中dia.base_data_能够被赋值,再看普通继承的例子。
普通继承样例:
1 class CTestBase 2 { 3 public: 4 int base_data_; 5 }; 6 7 class CTestSubject1:public CTestBase 8 { 9 public: 10 int subone_data_; 11 }; 12 13 class CTestSubject2:public CTestBase 14 { 15 public: 16 int subtwo_data_; 17 }; 18 19 class CTestDiamond:public CTestSubject1,public CTestSubject2 20 { 21 public: 22 int dia_data_; 23 } 24 25 int main() 26 { 27 CTestDiamond dia; 28 dia.base_data_ =1; 29 }
这时候就出问题了,同样的继承结构,但是只不过没有了虚拟继承,我们对dia.base_data_的赋值就不能通过编译了,这是因为没有了虚拟继承,我们的dia对象中有2个base_data_,一个是从CTestSubject1中继承来的,另一个是从CTestSubject2中继承来的。这样一来编译器就不知道具体要给哪个base_data_赋值了,而当我们用虚拟继承时,对象dia中只有一个base_data_,所以显而易见编译器就知道该怎么赋值。这就是那句话的意思,不管继承多少次,永远只会存在一个虚基类的实体。
那我们该怎么在普通继承中对多个base_data_赋值呢?
我们只需要dia.CTestSubject1::base_data_ = 1,这样编译器就知道我们是想给CTestSubject1中的base_data_赋值了。CTestSubject2里面的base_data_也同理。
总结:
通过上面的2个例子,我们很容易看出虚拟继承和普通继承的区别,我觉得学习语言不需要去纠结语法,而应该去理解各种语法用法之间的区别,具体怎么用,只有结合实际中的编程场景,通过分析选择最合适的方式来使用,相信存在即是道理。
当然上面只是从表面来分析虚拟继承和普通继承,更深层次地可以从对象模型和内存布局去理解。