虚继承(c++常问问题九)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了虚继承(c++常问问题九)相关的知识,希望对你有一定的参考价值。

*什么是虚继承,使用虚继承来解决什么问题,什么是虚基类

#虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如:类D继承自类B1、B2,而类B1、B2都继承自类A,因此在类D中两次出现类A中的变量和函数。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。
 
#在现实生活中,一些新事物往往会拥有两个或者两个以上事物的属性,为了解决这个问题,C++引入了多重继承的概念,C++允许为一个派生类指定多个基类,这样的继承结构被称做多重继承

举个例子,交通工具类可以派生出汽车和船连个子类,但拥有汽车和船共同特性水陆两用汽车就必须继承来自汽车类与船类的共同属性。 
技术分享

当一个派生类要使用多重继承的时候,必须在派生类名和冒号之后列出所有基类的类名,并用逗好分隔。

 

class Vehicle  
{  
    public:  
        Vehicle(int weight = 0)  
        {  
            Vehicle::weight = weight;  
            cout<<"载入Vehicle类构造函数"<<endl;  
        }  
        void SetWeight(int weight)  
        {  
            cout<<"重新设置重量"<<endl;  
            Vehicle::weight = weight;  
        }  
        virtual void ShowMe() = 0;  
    protected:  
        int weight;  
};  
class Car:virtual public Vehicle//汽车,这里是虚拟继承  
{  
    public:  
        Car(int weight=0,int aird=0):Vehicle(weight)  
        {  
            Car::aird = aird;  
            cout<<"载入Car类构造函数"<<endl;  
        }  
        void ShowMe()  
        {  
            cout<<"我是汽车!"<<endl;  
        }  
    protected:  
        int aird;  
};  
  
class Boat:virtual public Vehicle//船,这里是虚拟继承  
{  
    public:  
        Boat(int weight=0,float tonnage=0):Vehicle(weight)  
        {  
            Boat::tonnage = tonnage;  
            cout<<"载入Boat类构造函数"<<endl;  
        }  
        void ShowMe()  
        {  
            cout<<"我是船!"<<endl;  
        }  
    protected:  
        float tonnage;  
};  
  
class AmphibianCar:public Car,public Boat//水陆两用汽车,多重继承的体现  
{  
    public:  
        AmphibianCar(int weight,int aird,float tonnage)  
        :Vehicle(weight),Car(weight,aird),Boat(weight,tonnage)  
        //多重继承要注意调用基类构造函数  
        {  
            cout<<"载入AmphibianCar类构造函数"<<endl;  
        }  
        void ShowMe()  
        {  
            cout<<"我是水陆两用汽车!"<<endl;  
        }  
        void ShowMembers()  
        {  
            cout<<"重量:"<<weight<<"顿,"<<"空气排量:"<<aird<<"CC,"<<"排水量:"<<tonnage<<"顿"<<endl;  
        }  
};  
int main()  
{  
    AmphibianCar a(4,200,1.35f);  
    a.ShowMe();  
    a.ShowMembers();  
    a.SetWeight(3);  
    a.ShowMembers();  
    system("pause");   
}

 

进阶部分:虚继承的内存占用下面看代码:
(演示了虚继承和非虚继承时的内存..看前请先恶补一下C++的字节对齐(我在c++常见问题的开篇中分析过))
//
class a1
{
	 virtual void func();
};

class b1 : public virtual a1
{
	virtual void foo();
};

//
class a2
{
	virtual void func();
};

class b2 : public a2
{
	virtual void foo();
};

//
class a3
{
	virtual void func();
	char a;
};

class b3 : public virtual a3
{
	virtual void foo();
};

//
class a4
{
	virtual void func();
	char a;
};

class b4 : public a4
{
	virtual void foo();
};

void main()
{
	cout<<sizeof(a1)<<","<<sizeof(b1)<<endl;
	cout<<sizeof(a2)<<","<<sizeof(b2)<<endl;
	cout<<sizeof(a3)<<","<<sizeof(b3)<<endl;
	cout<<sizeof(a4)<<","<<sizeof(b4)<<endl;

	system("pause");
}

 

第一种:4,12
第二种:4,4

第三种:8,16

第四种:8,8

 

想想这是为什么呢?

    因为每个存在虚函数的类都要有一个4字节的指针指向自己的虚函数表,所以每种情况的类a所占的字节数应该是没有什么问题的,那么类b的字节数怎么算呢?“第一种”和“第三种”情况采用的是虚继承,那么这时候就要有这样的一个指针vptr_b_a,这个指针叫虚类指针,也是四个字节;还要包括类a的字节数,所以类b的字节数就求出来了。而“第二种”和“第四种”情况则不包括vptr_b_a这个指针,这回应该木有问题了吧。

 

以上是关于虚继承(c++常问问题九)的主要内容,如果未能解决你的问题,请参考以下文章

C++11 std::function用法(c++常问问题十七)

赋值构造函数(重载赋值操作符)(c++常问问题二)

子类有参构造函数调用基类有参构造函数(c++常问问题四)

友元(c++常问问题十三)

移动构造函数(c++常问问题十六)

C++多级虚继承编译问题