C++学习之多重继承与虚继承
一、多重继承
我们知道,在单继承中,派生类的对象中包含了基类部分 和 派生类自定义部分。同样的,在多重继承(multiple inheritance)关系中,派生类的对象包含了每个基类的子对象和自定义成员的子对象。下面是一个多重继承关系:
class A{ /* */ };
class B{ /* */ };
class C : public A { /* */ };
class D : public B, public C { /* */ };
C继承了A,派生类D又继承了B和C,如图所示,一个D对象中含有一个B部分、一个C部分(其中又含有一个A部分)以及在D中声明的非静态数据成员:
构造与析构:
构造一个派生类对象将首先构造它的所有基类子对象,其中基类的构造顺序与派生列表中基类的出现顺序保持一致,即B –> A –> C –> D。
销毁一个派生类对象的顺序正好与其创建的顺序相反,即析构函数的调用顺序正好与构造函数相反,即D –> C –> A –> B。注意派生类的析构函数只负责清除派生类本身分配的资源(析构函数体),派生类的成员及基类都是自动销毁的(隐式析构阶段)。
类型转换:
在多重继承的情况下,可以令某个可访问基类的指针或引用直接指向一个派生类对象。编译器不会在派生类向基类的几种转换中进行比较和选择,在它看来转换到任意一种基类都一样好。
二、虚继承
尽管在派生列表中不允许同一个基类出现两次,但实际上派生类可以多次继承同一个类。
派生类通常会含有继承链上每个类对应的子部分。在上面的两种情况中,class D都间接地继承了class A两次,那么意味着class D中包含了class A的两份拷贝。所以在一个class D的对象中将含有2组class A的成员,此时若不加前缀限定符直接使用某个成员将引发“二义性”错误:
class A{
public:
A():str("name"){};
string str;
void print(){cout << str << endl;};
};
class B : public A { };
class C : public A { };
class D : public B, public C { };
int main(){
D d;
d.str = "songlee"; // 错误:对成员‘str’的请求有歧义
d.print(); // 错误:对成员‘print’的请求有歧义
return 0;
}
当然你可以使用作用域 d.B::str="songlee"; 和 d.B::print(); 来规避“二义性”错误,但这并没有从根本上解决问题。
为了解决上述问题,C++提供了虚继承(virtual inheritance)的机制。虚继承的目的是令某个类作出声明,承诺愿意共享它的基类。其中,共享的基类子对象称为虚基类。在这种机制下,不论虚基类在继承体系中出现多少次,在派生类中都只包含唯一一个共享的虚基类子对象。我们指定虚基类的方式是在派生列表中添加关键字virtual:
class A{
public:
A():str("name"){};
string str;
void print(){cout << str << endl;};
};
class B : virtual public A { }; // 虚继承,A为虚基类
class C : virtual public A { }; // 关键字public和virtual的顺序随意
class D : public B, public C { };
int main(){
D d;
d.str = "songlee"; // 正确
d.print(); // 正确
return 0;
}
通过在派生列表中添加virtual(关键字public和virtual的顺序随意)指定A为虚基类,B和C将共享A的同一份实例,这样在D的对象中也将只有A的唯一一份实例,所以A的成员可以被直接访问,并且不会产生二义性。
虚继承最典型的应用是iostream继承于istream和ostream,而istream和ostream虚继承于ios:
class istream : virtual public ios { /* */ };
class ostream : virtual public ios { /* */ };
class iostream : public istream, public ostream { /* */ };
注意:
- 支持向基类的常规类型转换。也就是说即使基类是虚基类,也能通过基类的指针或引用操作派生类的对象。
- 虚继承只是解决了一个派生类对象中存在同一个基类的多份拷贝的问题,并没有解决多个基类存在同名成员的二义性问题。
- 在虚继承中,虚基类是由最低层的派生类负责初始化的。如上例中,当创建一个D对象时,D位于派生的最低层并由它负责初始化共享的A基类部分。
- 含有虚基类的对象的构造顺序与一般的多重继承的构造顺序稍有区别:先初始化虚基类子对象(最低层派生类负责),然后按派生列表中的顺序依次对直接基类(非虚)进行初始化。
- 析构的顺序与构造的顺序正好相反。
C++学习之多重继承与虚继承的更多相关文章
- C++ Primer 学习笔记_95_用于大型程序的工具 --多重继承与虚继承
用于大型程序的工具 --多重继承与虚继承 引言: 大多数应用程序使用单个基类的公用继承,可是,在某些情况下,单继承是不够用的,由于可能无法为问题域建模,或者会对模型带来不必要的复杂性. 在这些情况下, ...
- C++中的多重继承与虚继承的问题
1.C++支持多重继承,但是一般情况下,建议使用单一继承. 类D继承自B类和C类,而B类和C类都继承自类A,因此出现下图所示情况: A A \ / B C ...
- C++ Primer 有感(多重继承与虚继承)
1.多重继承的构造次序:基类构造函数按照基类构造函数在类派生列表中的出现次序调用,构造函数调用次序既不受构造函数初始化列表中出现的基类的影响,也不受基类在构造函数初始化列表中的出现次序的影响.2.在单 ...
- C++ Primer 笔记——多重继承与虚继承
1.在多重继承中,基类的构造顺序与派生类列表中基类的出现顺序保持一致,与初始值列表中的顺序无关. 2.在C++11新标准中,允许派生类从它的一个或几个基类中继承构造函数.但是如果从多个基类中继承了相同 ...
- 多重继承,虚继承,MI继承中虚继承中构造函数的调用情况
先来测试一些普通的多重继承.其实这个是显而易见的. 测试代码: //测试多重继承中派生类的构造函数的调用顺序何时调用 //Fedora20 gcc version=4.8.2 #include < ...
- 【c++】多重继承与虚继承
派生类的构造函数初始化列表将实参分别传递给每个直接基类,其中基类的构造顺序与派生列表中基类的出现顺序保持一致,而与派生类构造函数初始化列表中基类的顺序无关. 类型转换与多个基类 编译器不会在派生类向基 ...
- C++继承,多重继承,虚继承的构造函数以及析构函数的调用顺序问题
#include <iostream> using namespace std; class A{ int data_a; public: A(){ data_a = ; cout < ...
- Linux Debugging(四): 使用GDB来理解C++ 对象的内存布局(多重继承,虚继承)
前一段时间再次拜读<Inside the C++ Object Model> 深入探索C++对象模型,有了进一步的理解,因此我也写了四篇博文算是读书笔记: Program Transfor ...
- (C/C++学习)5.C++中的虚继承-虚函数-多态解析
说明:在C++学习的过程中,虚继承-虚函数经常是初学者容易产生误解的两个概念,它们与C++中多态形成的关系,也是很多初学者经常产生困惑的地方,这篇文章将依次分别对三者进行解析,并讲述其之间的联系与不同 ...
随机推荐
- 关于vue项目 路由中 使用的坑
关于vue路由重定向的时候 记得一定要先声明先声明
- Tomcat无法clean,无法remove部署的项目
错误: 对部署在Tomcat下的项目进行clean操作,总是提示Could not load the Tomcat server configuration,错误信息如图: 解决: 原来是将Serve ...
- 上传一个npm包
1.先创建一个npm账号 https://www.npmjs.com/signup 2.在cmd里输入命令进入项目文件夹 3.使用npm init 命令创建一个package.json(确保nodej ...
- CAD得到范围内实体(网页版)
主要用到函数说明: IMxDrawSelectionSet::Select 构造选择集.详细说明如下: 参数 说明 [in] MCAD_McSelect Mode 构造选择集方式 [in] VARIA ...
- Vue.js 计算属性(computed)
Vue.js 计算属性(computed) 如果在模板中使用一些复杂的表达式,会让模板显得过于繁重,且后期难以维护.对此,vue.js 提供了计算属性(computed),你可以把这些复杂的表达式写到 ...
- HTTP服务和APACHE2
HTTP服务和APACHE2 知识点 请求报文响应报文 错误码 请求重定向 编译安装 实现https curl工具 1. http协议 http协议版本 http/0.9, http/1.0, htt ...
- Ubuntu下安装oh-my-zsh
本文参考 https://www.jianshu.com/p/9a5c4cb0452d 此文已在ubuntu下确实安装成功,只不过懒得截图了,可以参照上述地址,我在他原基础上进行了一些更改. 安装Oh ...
- reading/writing files in Python
file types: plaintext files, such as .txt .py Binary files, such as .docx, .pdf, iamges, spreadsheet ...
- 洛谷 4768 LOJ 2718「NOI2018」归程
[题解] 本题有多种做法,例如可持久化并查集.kruskal重构树等. kruskal重构树的做法是这样的:先把边按照海拔h从大到小的顺序排序,然后跑kruskal建立海拔的最大生成树,顺便建krus ...
- spring与quartz整合实现分布式动态创建,删除,改变执行时间定时任务(mysql数据库)
背景:因为在项目中用到了定时任务,当时想到了spring的quartz,写完发现费了很大功夫,光是整合就花了一上午,其中最大的问题就是版本问题,项目中用的是spring3.2.8的版本,查阅发现,3. ...