前言:虚继承是为了解决多重继承出现菱形继承时出现的问题。例如:类B、C分别继承了类A。类D多重继承类B和C的时候,类A中的数据就会在类D中存在多份。通过声明继承关系的时候加上virtual关键字可以实现虚继承。

一、多重继承

单重继承——一个派生类最多只能有一个基类
多重继承——一个派生类可以有多个基类

class 类名: 继承方式 基类1,继承方式 基类2,…. {….};

派生类同时继承多个基类的成员,更好的软件重用
可能会有大量的二义性,多个基类中可能包含同名变量或函数
 

多重继承中解决访问歧义的方法:

基类名::数据成员名(或成员函数(参数表))

明确指明要访问定义于哪个基类中的成员
#include <iostream> using namespace std; class Bed { public: Bed(int weight) :
weight_(weight) { } void Sleep() { cout << "Sleep ..." << endl; } int weight_;
}; class Sofa { public: Sofa(int weight) : weight_(weight) { } void WatchTV() {
cout << "Watch TV ..." << endl; } int weight_; }; class SofaBed : public Bed,
public Sofa { public: SofaBed() : Bed(0), Sofa(0) { FoldIn(); } void FoldOut()
{ cout << "FoldOut ..." << endl; } void FoldIn() { cout << "FoldIn ..." <<
endl; } }; int main(void) { SofaBed sofaBed; //sofaBed.weight_ = 10; error
//sofaBed.weight_ = 20; error sofaBed.Bed::weight_ = 10; sofaBed.Sofa::weight_
= 20; sofaBed.WatchTV(); sofaBed.FoldOut(); sofaBed.Sleep(); return 0; }
不能直接写 sofaBed.weight_ = 10; 因为sofaBed 继承了Sofa 和 Bed
,实际上有weigh_的两份拷贝,这样指向不明。只能通过

sofaBed.Bed::weight_ = 10; 访问,但实际上一个sofaBed理应只有一个weight_,下面通过虚基类和虚继承可以解决这个问题。

二、虚继承与虚基类

当派生类从多个基类派生,而这些基类又从同一个基类派生,则在访问此共同基类中的成员时,将产生二义性,可以采用虚基类来解决。

虚基类的引入

用于有共同基类的场合

声明

以virtual修饰说明基类 例:class B1:virtual public BB

作用

主要用来解决多继承时可能发生的对同一基类继承多次而产生的二义性问题.

为最远的派生类提供唯一的基类成员,而不重复产生多次拷贝

       class CBase { };

  class ChildA1:virtual public CBase{ };

  class ChildA2:virtual public CBase{ };

  class ChildB:public ChildA1,ChildA2{ };

  则在类ChildB的对象中,仅有类CBase的一个对象数据

虚基类的特点:

        (1)虚基类构造函数的参数必须由最新派生出来的类负责初始化(即使不是直接继承).       

   (2)虚基类的构造函数先于非虚基类的构造函数执行。

下面看一段程序的输出结果:
#include "stdafx.h" #include<iostream> using namespace std; class CBase{
protected: int a; public: CBase(int na) { a = na; cout << "CBase constructor!"
<< endl; } ~CBase() { cout << "CBase deconstructor!" << endl; } }; class
ChildA1: virtual public CBase { public: ChildA1(int na):CBase(na) { cout <<
"ChildA1 constructor!" << endl; } ~ChildA1() { cout << "ChildA1 deconstructor!"
<< endl; } int GetA() { return a; } }; class ChildA2 : virtual public CBase {
public: ChildA2(int na):CBase(na) { cout<< " ChildA2 constructor!" << endl; }
~ChildA2() { cout<< "ChildA2 deconstructor!" << endl; } int GetA() { return a;
} }; class ChildB:public ChildA1,public ChildA2 { public: ChildB(int a1,int
a2,int a3):ChildA1(a1),ChildA2(a2),CBase(a3) { cout << "ChildB constructor!!"
<< endl; } ~ChildB() { cout << "ChildB deconstructor!" << endl; } }; int main()
{ ChildB childb(100,200,300); //得到从ChildA1继承的值 cout<<" from ChildA1 : a =
"<<childb.ChildA1::GetA(); //得到从ChildA2继承的值 cout<<" from ChildA2 : a =
"<<childb.ChildA2::GetA()<<endl<<endl; return 0; }
程序输出的结果:

 

从上例中可以看出来,在类ChildB的构造函数初始列表中,调用了间接基类CBase的构造函数,这对于非基类是非法的,但对于虚基类则是合法而且是必要的。

  从输出的结果可以看出来,其公共基类的构造函数只调用了一次,并且优先于非虚基类的构造函数调用
,并且发现,子派生类的对象childb的成员变量的值只有一个,所以当公共基类CBase被声明为虚基类,虽然它成为ChildA1和ChildA2的公共基类,但子派生类ChildB中也只有它的一个备份.

 

 

技术
©2019-2020 Toolsou All rights reserved,
排序(一)冒泡排序法一文揭秘阿里、腾讯、百度的薪资职级大数据告诉你,中国女人有多累年薪20万属于什么水平?答案让人扎心!面试的时候突然遇到答不上的问题怎么办?SpringBoot实践(五):mybatis-plus中的BaseMapper,Iservice和ServiceImpl这些歌,程序员千万万万万别听!python中解决字典写入列表的问题中台透彻讲解GDOI2019 游记