◇概念:
C++使用虚拟继承(Virtual Inheritance),解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。
◇解决问题:
解决了二义性问题,也节省了内存,避免了数据不一致的问题。
◇同义词:
虚基类(把一个动词当成一个名词而已)
当在多条继承路径上有一个公共的基类,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个公共基类说明为虚基类。
◇语法:
class 派生类: virtual 基类1,virtual 基类2,...,virtual 基类n
{
...//派生类成员声明
};
◇执行顺序:
首先执行虚基类的构造函数,多个虚基类的构造函数按照被继承的顺序构造;
执行基类的构造函数,多个基类的构造函数按照被继承的顺序构造;
执行成员对象的构造函数,多个成员对象的构造函数按照申明的顺序构造;
执行派生类自己的构造函数;
析构以与构造相反的顺序执行;
◇因果:
多重继承->二义性->虚拟继承解决
◇二义性:
1 #include<iostream> 2 using namespace std; 3 4 class base 5 { 6 public: 7 base() 8 { 9 cout<<"base called..."<<endl; 10 } 11 void print() 12 { 13 cout<<"base print...."<<endl; 14 } 15 16 private: 17 18 }; 19 20 21 class sub 22 { 23 public: 24 sub() 25 { 26 cout<<"sub called..."<<endl; 27 } 28 void print() 29 { 30 cout<<"sub print...."<<endl; 31 } 32 33 private: 34 35 }; 36 37 38 class child:public base,public sub 39 { 40 public: 41 child() 42 { 43 cout<<"child called..."<<endl; 44 45 } 46 private: 47 }; 48 49 void main() 50 { 51 child c; 52 //c.print(); //error!找不到标识符 53 54 c.base::print(); 55 c.sub::print(); 56 57 char wait; 58 cin>>wait; 59 60 }
运行结果:
base called...
sub called...
child called...
base print...
sub print...
◇多重继承:
1 #include<iostream> 2 using namespace std; 3 4 int num=0; 5 6 class base 7 { 8 public: 9 base() 10 { 11 cout<<"base called..."<<num++<<endl; 12 } 13 void print() 14 { 15 cout<<"base print...."<<endl; 16 } 17 18 private: 19 20 }; 21 22 23 class Mid1:public base 24 { 25 public: 26 Mid1() 27 { 28 cout<<"Mid1 called..."<<endl; 29 } 30 //void print() 31 //{ 32 // cout<<"Mid1 print...."<<endl; 33 //} 34 35 private: 36 37 }; 38 39 class Mid2:public base 40 { 41 public: 42 Mid2() 43 { 44 cout<<"Mid2 called..."<<endl; 45 } 46 //void print() 47 //{ 48 // cout<<"Mid2 print...."<<endl; 49 //} 50 51 private: 52 53 }; 54 55 56 class child:public Mid1,public Mid2 57 { 58 public: 59 child() 60 { 61 cout<<"child called..."<<endl; 62 63 } 64 private: 65 }; 66 67 void main() 68 { 69 child c; 70 //c.print(); //error!找不到标识符 71 72 c.Mid1::print(); 73 c.Mid2::print(); 74 75 76 char wait; 77 cin>>wait; 78 79 }
运行结果:
Base called : 0 Mid1 called Base called : 1 Mid2 called Child called Base print Base print
◇虚拟继承:
在派生类继承基类时,加上一个virtual关键词则为虚拟继承
1 #include<iostream> 2 using namespace std; 3 4 int num=0; 5 6 class base 7 { 8 public: 9 base() 10 { 11 cout<<"base called..."<<num++<<endl; 12 } 13 void print() 14 { 15 cout<<"base print...."<<endl; 16 } 17 18 private: 19 20 }; 21 22 23 class Mid1:virtual public base 24 { 25 public: 26 Mid1() 27 { 28 cout<<"Mid1 called..."<<endl; 29 } 30 //void print() 31 //{ 32 // cout<<"Mid1 print...."<<endl; 33 //} 34 35 private: 36 37 }; 38 39 class Mid2:virtual public base 40 { 41 public: 42 Mid2() 43 { 44 cout<<"Mid2 called..."<<endl; 45 } 46 //void print() 47 //{ 48 // cout<<"Mid2 print...."<<endl; 49 //} 50 51 private: 52 53 }; 54 55 56 class child:public Mid1,public Mid2 57 { 58 public: 59 child() 60 { 61 cout<<"child called..."<<endl; 62 63 } 64 private: 65 }; 66 67 void main() 68 { 69 child c; 70 //c.print(); //error!找不到标识符 71 72 c.Mid1::print(); 73 c.Mid2::print(); 74 75 76 char wait; 77 cin>>wait; 78 79 }
运行结果:
Base called : 0Mid1 called Mid2 called Child called Base print Base print
◇通过输出的比较
1.在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。
2.声明了虚基类之后,虚基类在进一步派生过程中始终和派生类一起,维护同一个基类子对象的拷贝。
3.观察类构造函数的构造顺序,拷贝也只有一份。
◇与虚函数关系
虚拟继承与虚函数有一定相似的地方,但他们之间是绝对没有任何联系的。
再想一次:虚拟继承,虚基类,虚函数。
◇菱形结构虚继承对象的内存模型
来源:https://www.cnblogs.com/2007winter/archive/2012/10/16/2725714.html