C++虚继承解说
1.概括
在CPlusPlus多继承编程中时常遇到这样一个问题--若子类实现多个基类或接口继承,多基类或接口中存在成员名相同,在客户与实现类之间的通信时编译器报错“不能这样使用,会产生二义性”由于这个问题的解决方法很多。比如说,可以把相同的成员名给改过来。但是,从专业的角度,可能虚拟继承会解决这个问题。那接下来我看看c++是怎么避免这种问题的。
2.概念
当在多条继承路径上有一个公共的基类,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个公共基类说明为虚基类。 class 派生类名:virtual 继承方式 基类名
virtual是关键字,声明该基类为派生类的虚基类。
例如:
class派生类:virtual基类1,virtual基类2,...,virtual基类n{...//派生类成员声明};
在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。声明了虚基类之后,虚基类在进一步派生过程中始终和派生类一起,维护同一个基类子对象的拷贝。C++使用虚拟继承(Virtual Inheritance),解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。这样带来的有点是解决了二义性问题,也节省了内存,避免了数据不一致的问题。
3.用例
二义性:
#include<iostream>usingnamespacestd;//BaseclassBase{public:Base(){cout<<"Basecalled..."<<endl;}voidprint(){cout<<"Baseprint..."<<endl;}private:};//SubclassSub//定义一个类Sub{public:Sub(){cout<<"Subcalled..."<<endl;}voidprint(){cout<<"Subprint..."<<endl;}private:};//ChildclassChild:publicBase,publicSub//定义一个类Child分别继承自Base,Sub{public:Child(){cout<<"Childcalled..."<<endl;}private:};intmain(intargc,char*argv[]){Childc;//不能这样使用,会产生二意性,VC下errorC2385//c.print();//只能这样使用c.Base::print();c.Sub::print();system("pause");return0;}
多重继承:
//说明:C++虚拟继承学习演示//环境:VS2005//blog:pppboy.blog.163.com//----------------------------------------------------#include"stdafx.h"#include<iostream>usingnamespacestd;intgFlag=0;classBase{public:Base(){cout<<"Basecalled:"<<gFlag++<<endl;}voidprint(){cout<<"Baseprint"<<endl;}};classMid1:publicBase{public:Mid1(){cout<<"Mid1called"<<endl;}private:};classMid2:publicBase{public:Mid2(){cout<<"Mid2called"<<endl;}};classChild:publicMid1,publicMid2{public:Child(){cout<<"Childcalled"<<endl;}};intmain(intargc,char*argv[]){Childd;//不能这样使用,会产生二意性//d.print();//只能这样使用d.Mid1::print();d.Mid2::print();system("pause");return0;}
output:
Basecalled:0
Mid1called
Basecalled:1
Mid2called
Childcalled
Baseprint
Baseprint
虚拟继承:
#include"stdafx.h"#include<iostream>usingnamespacestd;intgFlag=0;classBase{public:Base(){cout<<"Basecalled:"<<gFlag++<<endl;}voidprint(){cout<<"Baseprint"<<endl;}};classMid1:virtualpublicBase{public:Mid1(){cout<<"Mid1called"<<endl;}private:};classMid2:virtualpublicBase{public:Mid2(){cout<<"Mid2called"<<endl;}};classChild:publicMid1,publicMid2{public:Child(){cout<<"Childcalled"<<endl;}};intmain(intargc,char*argv[]){Childd;//这里可以这样使用d.print();//也可以这样使用d.Mid1::print();d.Mid2::print();system("pause");return0;}
4.总结
在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。声明了虚基类之后,虚基类在进一步派生过程中始终和派生类一起,维护同一个基类子对象的拷贝。观察类构造函数的构造顺序,拷贝也只有一份。
5.扩展
windows编程的程序员们在进行COM编程的时候会遇到这样的一个问题------继承接口IUnknown这一块使用非虚拟继承。这是为什么呢?如果有这样的疑问是很正常。之所以这样是由于会导致与COM不兼容的vtbl。比如:
struct IX : public IUnknown
{
//....
};
struct IY : public IUnknow
{
//....
};
客户程序实现:
...if(iid==IID_IUnknown){//theclientwantstheIUnknowninterface.*ppv=static_cast<IX*>(this);}elseif(iid==IID_IX){//theclientwantstheIXinterface.*ppv=static_cast<IX*>(this);}elseif(iid==IID_IY){*ppv=static_cast<IY*>(this);}...
可见,程序中他们是通过类型转换的。不然,IX和IY的vtbl中的头三个函数指向的将不是IUnknown的三个成员函数。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。