多重继承,虚继承,MI继承中虚继承中构造函数的调用情况
先来测试一些普通的多重继承。其实这个是显而易见的。
测试代码:
- //测试多重继承中派生类的构造函数的调用顺序何时调用
- //Fedora20 gcc version=4.8.2
- #include <iostream>
- using namespace std;
- class base
- {
- public:
- base()
- {
- cout<<"base created!"<<endl;
- }
- ~base()
- {
- cout<<"base destroyed!"<<endl;
- }
- };
- //公有继承
- class A:public base
- {
- public:
- A()
- {
- cout<<"A created!"<<endl;
- }
- ~A()
- {
- cout<<"A destroyed!"<<endl;
- }
- };
- class B:public base
- {
- public:
- B()
- {
- cout<<"B created!"<<endl;
- }
- ~B()
- {
- cout<<"B destroyed!"<<endl;
- }
- };
- class C:public B,public A//多重继承
- //改为class C:public A,public B后,AB构造的顺序改变
- {
- public:
- C(){
- cout<<"C created!"<<endl;
- }
- ~C(){
- cout<<"C destroyed"<<endl;
- }
- };
- int main()
- {
- C test;
- }
测试结果:
显而易见,普通的多重基层依次调用上一层父类的构造函数。这就说明了MI有时候不太好的情况了,那就是如果你不小心的话,就会从上一层继承关系中意外创建多个基类的对象,例如如果base里面有一个string name属性,那么D创建的时候就会创建两个string name的对象,而这个,不一定是你想要的结果。
现在,来测试一下父类中存在虚继承的情况。
测试代码:
- //测试多重继承中派生类的构造函数的调用顺序何时调用
- //Fedora20 gcc version=4.8.2
- #include <iostream>
- using namespace std;
- class base
- {
- public:
- base(){
- cout<<"base created!"<<endl;
- }
- ~base(){
- cout<<"base destroyed!"<<endl;
- }
- };
- //虚继承
- class A:public virtual base
- {
- public:
- A(){
- cout<<"A created!"<<endl;
- }
- ~A() {
- cout<<"A destroyed!"<<endl;
- }
- };
- //虚继承
- class B:public virtual base
- {
- public:
- B(){
- cout<<"B created!"<<endl;
- }
- ~B(){
- cout<<"B destroyed!"<<endl;
- }
- };
- //C是虚继承base
- class C:public virtual base
- {
- public:
- C(){
- cout<<"C created!"<<endl;
- }
- ~C(){
- cout<<"C destroyed"<<endl;
- }
- };
- class D:public A,public B,public C
- {//D A,B,C都是虚继承的形式。
- public:
- D(){
- cout<<"D created!"<<endl;
- }
- ~D(){
- cout<<"D destroyed!"<<endl;
- }
- };
- int main()
- {
- D d;
- }
测试结果:
可以看到,如果上一层继承中都是虚继承,那么,只会在最开始一次调用base基类构造函数。
那么,如果A不是虚继承呢?
- //A不是虚继承
- class A:public base
- {
- public:
- A(){
- cout<<"A created!"<<endl;
- }
- ~A() {
- cout<<"A destroyed!"<<endl;
- }
- };
测试结果:
看到虚继承的B,C依旧只是在最开始使调用了一次base,但是A类不再是虚继承,因此A类的构造函数也调用来一次base的构造函数.
[admin@localhost csuper]$ ./c
base created! //这是BC共同调用的base构造函数
base created! //这是调用A类的构造函数时,A类构造函数又调用了一次base的构造函数。
A created!
为了测试这一想法是否真是如此,这里我们利用控制变量法,仅使B类不是虚继承。
- //仅令B不是虚继承,A,C依旧是虚继承
- class B:public base
- {
- public:
- B(){
- cout<<"B created!"<<endl;
- }
- ~B(){
- cout<<"B destroyed!"<<endl;
- }
- };
结果:
可以看出,结果正是如此。
同时发现了一个有趣的情况。当A,B,C都是虚继承base的时候,D虚继承C,看看结果又会如何?
- class D:public A,public B,virtual public C
- {// A,B,C都是虚继承base的形式。
- //D又虚继承C
- public:
- D(){
- cout<<"D created!"<<endl;
- }
- ~D(){
- cout<<"D destroyed!"<<endl;
- }
- };
结果:
可以看到,构造D的对象时,先调用了base,然后就到了调用C的构造函数了,说明编译器在构造的时候,是优先构造虚继承的对象的,这样就保证了构造A,B对象的时候,如果AB是虚继承于base,就不会创建多个从base定义的成员属性了。
但是如果C不是虚继承base,但D又是虚继承C的时候又会如何呢?
- //C不是虚继承base
- class C:public base
- {
- public:
- C(){
- cout<<"C created!"<<endl;
- }
- ~C(){
- cout<<"C destroyed"<<endl;
- }
- };
- class D:public A,public B,virtual public C
- {//D A,B,C都是虚继承的形式。
- public:
- D(){
- cout<<"D created!"<<endl;
- }
- ~D(){
- cout<<"D destroyed!"<<endl;
- }
- };
结果:
可以看出,第一个调用的base应该是属于A,B调用的,因此,其实上面的说法是不对的,因为如果是优先调用C的构造函数,输出应该是
base created! //如果是优先调用C
C created!
base created! //A,B调用的base应该在这里出现,但事实上却不是。
A created!
B created!
D created!
...........
再看看下面的测试:
- //测试多重继承中派生类的构造函数的调用顺序何时调用
- //Fedora20 gcc version=4.8.2
- #include <iostream>
- using namespace std;
- class base
- {
- public:
- base(){
- cout<<"base created!"<<endl;
- }
- ~base(){
- cout<<"base destroyed!"<<endl;
- }
- };
- //A是虚继承
- class A:public virtual base
- {
- public:
- A(){
- cout<<"A created!"<<endl;
- }
- ~A() {
- cout<<"A destroyed!"<<endl;
- }
- };
- //B不是虚继承
- class B:public base
- {
- public:
- B(){
- cout<<"B created!"<<endl;
- }
- ~B(){
- cout<<"B destroyed!"<<endl;
- }
- };
- //C不是虚继承base
- class C:public base
- {
- public:
- C(){
- cout<<"C created!"<<endl;
- }
- ~C(){
- cout<<"C destroyed"<<endl;
- }
- };
- class D:public A,public virtual B,virtual public C
- {// B,C都是虚继承的形式。
- public:
- D(){
- cout<<"D created!"<<endl;
- }
- ~D(){
- cout<<"D destroyed!"<<endl;
- }
- };
- int main()
- {
- D d;
- }
这里只有A是虚继承base,B,C都不是虚继承,但是在D里面,刚好相反,看看结果如何
说实在的,输出是这样我是没有想到的。
base created! //这个base是哪一个调用的呢?
base created!
B created!
base created!
C created!
后来我再这样测试一下我就知道是为什么了。
令A,B,C均不虚继承与base,但是D继承与A,并且虚继承B,C,看结果
因此可以看出,上一次的第一个base是因为A而调用的base,也就是说,编译器是先检测上一层继承关系(A,B,C)中,哪一个是虚继承于再上一层的类(base),如果有,则优先调用该虚继承与base的类(也就是A)的基类的(也就是base)构造函数,然后再调用D中虚继承的类的构造函数(B,C),最后才调用A自己的构造函数,这里有点复杂,还是上一次测试的例子:
base created! //这个base是因为A是虚继承与base时调用的,但是调用完之后并不直接调用A (A created!)
base created! //这个是调用B时调用的base
B created!
base created! //这个是调用C时i调用的base
C created!
A created! //最后才调用A created!
因此,可以发现,如果你不想该类(例如A)在被继承的时候防止派生类(D)多次创建该类(A)的基类(base)的多个对象,那么,就应该将该类声明为virtual 继承基类(base),vitual的作用是对下一层次的派生类起作用,对该类(A)并不起特别作用(但是该类(D)会优先调用虚继承类(B,C)的构造函数)。
多重继承,虚继承,MI继承中虚继承中构造函数的调用情况的更多相关文章
- sqlserver中,查看某个函数的调用情况
今天想在sqlserver中看看自己写的函数都被哪个函数或存储过程调用了,手工检查起来太慢了,于是在网上找一个快速的方法,分享一下. select * from sys.all_sql_modules ...
- C++中的继承和多继承
一.学习笔记 1.继承 class Student : public Person { ... } 2.继承时权限派生类中可以直接访问父类的protected成员,但是不能访问其private成员,若 ...
- C++中的继承与虚函数各种概念
虚继承与一般继承 虚继承和一般的继承不同,一般的继承,在目前大多数的C++编译器实现的对象模型中,派生类对象会直接包含基类对象的字段.而虚继承的情况,派生类对象不会直接包含基类对象的字段,而是通过一个 ...
- C++多重继承分析——《虚继承实现原理(虚继承和虚函数)》
博客转载:https://blog.csdn.net/longlovefilm/article/details/80558879 一.虚继承和虚函数概念区分 虚继承和虚函数是完全无相关的两个概念. 虚 ...
- C++中虚函数继承类的内存占用大小计算
计算一个类对象的大小时的规律: 1.空类.单一继承的空类.多重继承的空类所占空间大小为:1(字节,下同): 2.一个类中,虚函数本身.成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空 ...
- C++多态中虚函数表合并与继承问题
多态: C++的多态是通过一张虚函数表(Virtual Table)来实现的,简称为 V-Table.在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承.覆写的问题,保证其真实反应实际的函数. ...
- C++中虚继承派生类构造函数的正确写法
最近工作中某个软件功能出现了退化,追查下来发现是一个类的成员变量没有被正确的初始化.这个问题与C++存在虚继承的情况下派生类构造函数的写法有关.在此说明一下错误发生的原因,希望对更多的人有帮助. 我们 ...
- C++ 在继承中虚函数、纯虚函数、普通函数,三者的区别
1.虚函数(impure virtual) C++的虚函数主要作用是“运行时多态”,父类中提供虚函数的实现,为子类提供默认的函数实现. 子类可以重写父类的虚函数实现子类的特殊化. 如下就是一个父类中的 ...
- C++继承中析构函数 构造函数的调用顺序以及虚析构函数
首先说说构造函数.大家都知道构造函数里就能够调用成员变量,而继承中子类是把基类的成员变成自己的成员,那么也就是说子类在构造函数里就能够调用基类的成员了,这就说明创建子类的时候必须先调用基类的构造函数, ...
随机推荐
- 关于Spring的配置文件的注解使用
从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法, 这些方法将会AnnotationConfigApplic ...
- java中集合格式及json格式的特点和转换
作者原创:转载请注明出处 今天在写代码,遇到一个难点,由于要调用webservice接口,返回的为一个list集合内容,从webservice调用接口返回的为一个string的io流, 在调用接口的地 ...
- HDU 5876 Sparse Graph(补图中求最短路)
http://acm.hdu.edu.cn/showproblem.php?pid=5876 题意: 在补图中求s到其余各个点的最短路. 思路:因为这道题目每条边的距离都是1,所以可以直接用bfs来做 ...
- HDU 3466 Proud Merchants(0-1背包)
http://acm.hdu.edu.cn/showproblem.php?pid=3466 题意: 最近,iSea去了一个古老的国家.在这么长的时间里,它是世界上最富有和最强大的王国.结果,这个国家 ...
- nginx缓存功能的设置
首先用的缓存是proxy_cache. 在http段里加入下列几句: [plain] view plain copy proxy_connect_timeout 5; proxy_read_tim ...
- HTTP错误 404.17 - Not Found" IIS 7.5 请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理
Errore HTTP 404.2 - Not Found" IIS 7.5 请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理 出现这种情况的原因通常是因为先安装了Framewo ...
- UOJ【UR #12】实验室外的攻防战
题意: 给出一个排列$A$,问是否能够经过以下若干次变换变为排列$B$ 变换:若${A_i> A_i+1}$,可以${swap(A_i,A_i+1)}$ 考虑一个数字从A排列到B排列连出来的路径 ...
- ubuntu server 多网卡
https://wenku.baidu.com/view/51fb15742f60ddccdb38a007.html
- URAL 1741 Communication Fiend
URAL 1741 思路: dp 状态:dp[i][1]表示到第i个版本为正版的最少流量花费 dp[i][0]表示到第i个版本为盗版的最少流量花费 初始状态:dp[1][0]=dp[0][0]=0 目 ...
- 【python】模块整理
---文件.系统--- import glob # 给定路径下查找符合规则文件.三个匹配符:”*”, “?”, “[]”.”*”匹配0个或多个字符:”?”匹配单个字符:”[]”匹配指定范围内的字符, ...