欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++虚继承与继承

程序员文章站 2022-07-15 16:52:02
...

虚继承
1、why用虚继承?
为了解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。这样不仅就解决了二义性问题,也节省了内存,避免了数据不一致的问题。
class 派生类名:virtual 继承方式 基类名
virtual是关键字,声明该基类为派生类的虚基类。
在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。
声明了虚基类之后,虚基类在进一步派生过程中始终和派生类一起,维护同一个基类子对象的拷贝
◇同义词: 虚基类(把一个动词当成一个名词而已)
当在多条继承路径上有一个公共的基类,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个公共基类说明为虚基类。

◇语法:
class 派生类: virtual 基类1,virtual 基类2,…,virtual 基类n
{
…//派生类成员声明
};

◇执行顺序
首先执行虚基类的构造函数,多个虚基类的构造函数按照被继承的顺序构造;
执行基类的构造函数,多个基类的构造函数按照被继承的顺序构造;
执行成员对象的构造函数,多个成员对象的构造函数按照申明的顺序构造;
执行派生类自己的构造函数;
析构以与构造相反的顺序执行;
mark
从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象只初始化一次。
在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。

◇因果:
多重继承->二义性->虚拟继承解决

在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。
继承与虚继承
先来看普通的继承:

class base
{
public:
    base(){cout<<"base::base()!"<<endl;}
    void printBase(){cout<<"base::printBase()!"<<endl;}
};

class derived:public base
{
public:
    derived(){cout<<"derived::derived()!"<<endl;}
    void printDerived(){cout<<"derived::printDerived()!"<<endl;}
};

实际应用中,我们可以如下用:

int main(int argc, const char * argv[])
{
    derived oo;
    base oo1(static_cast<base>(oo));
    oo1.printBase();
    derived oo2 = static_cast<derived&>(oo1);
    oo2.printDerived();
    return 0;
}

编译无误也会得到正确的结果:

 base::base()!
 derived::derived()!
 base::printBase()!
 derived::printDerived()!

但是如果将普通的继承变成虚继承,如下:

class base1
{
public:
    base1(){cout<<"base::base()!"<<endl;}
    void printBase(){cout<<"base::printBase()!"<<endl;}
};

class derived1:virtual public base1
{
public:
    derived1(){cout<<"derived::derived()!"<<endl;}
    void printDerived(){cout<<"derived::printDerived()!"<<endl;}
};

int main(int argc, const char * argv[])
{
    derived1 oo;
    base1 oo1(static_cast<base1>(oo));
    oo1.printBase();
    derived1 oo2 = static_cast<derived1&>(oo1);
    oo2.printDerived();
    return 0;
}

这样使用,编译就会报错,提示我们不能将基类,通过static_cast转换为子类,普通继承是可以的,但是虚继承这里就变成不可以了,为什么呢?归根结底,还是要知道,虚继承已完全破坏了继承体系,不能按照平常的继承体系来进行类型转换。
c++ 没有禁止虚基类到派生类的转换,但是这样的行为是未定义的,一般最好不要这样做。

关于c++继承关系中基类子类相互转换
在继承层次中,存在向上指针类型转换或者向下类型转换,则调用成员函数(两个类都实现了)调用的是哪个类的函数,遵循下面2个规则:
(1)调用虚函数时,因为是动态绑定,所以根据指针指向的对象的实际类型来决定。
(2)调用非虚函数,静态决定,所以根据表面山看到的类的类型来决定。

参考:
http://blog.csdn.net/crystal_avast/article/details/7678704
http://blog.csdn.net/dqjyong/article/details/8029527#

相关标签: 虚继承