C++之虚拟继承和虚基类
来源:互联网 发布:隐形眼镜 知乎 编辑:程序博客网 时间:2024/06/05 05:34
昨天的两篇文章《C++之类的继承》和《C++之虚函数》带领我们认识了C++中的类的继承方法,以及多态的实现。今天我们来学习一下二者的结合体——虚拟继承和虚基类。
在讲解什么是虚拟继承之前,我们需要先普及一下子类和父类在内存中的关系。如图1所示,每一个子类在内存中都拥有父类内存的一份拷贝。
图1 子类和父类在内存中的关系
子类对象占用的内存始终会比父类对象占用的内存大,因为子类继承父类,也就继承了父类的方法接口,所以在子类对象中有一份父类的内存拷贝。
接下来说说单一继承和菱形继承。
1)单一继承是指每一个子类都只有一个父类,如图2所示。单一继承的内存关系如图3所示。
图2 单一继承 图3 单一继承的内存关系
2)菱形继承是指一个类的两个父类有共同的父类。如图4所示。菱形继承的内存关系如图5所示。
图4 菱形继承 图5 菱形继承的内存关系
当我们看到菱形继承的时候,是不是发现一点问题。第一,类C中包含类A的两份一模一样的拷贝。第二,类A的构造函数被重复调用了两次。如果读者不相信,可以试一试下面的代码。
程序的运行结果如下:#include <iostream>using namespace std;class A
{public:
A(){cout<<"This is A constructor."<<endl;}
~A(){cout<<"This is A destructor."<<endl;}
void speak(){cout<<"This is A speak!"<<endl;}};class B1: public A{public:
B1(){cout<<"This is B1 constructor."<<endl;}
~B1(){cout<<"This is B1 destructor."<<endl;}
};class B2: public A{public:
B2(){cout<<"This is B2 constructor."<<endl;}
~B2(){cout<<"This is B2 destructor."<<endl;}
};class C:public B1,public B2{public:
C(){cout<<"This is C constructor."<<endl;}
~C(){cout<<"This is C destructor."<<endl;}
};int main()
{C *c=new C();
//c->speak();
delete c;
return 0;
}
如果我们在代码中再添加如下代码:“c->speak();”,编译器还会报出错误。错误信息如下:
Error 1 error C2385: ambiguous access of 'speak' e:\XXX\main.cpp 38
这是由于类B1和类B2都有函数speak(),编译器不知道应该执行哪一个,此处产生了二义性。
综上所述,菱形继承主要是一下三个问题:
- 存储问题:占用了更多的存储空间。
- 效率问题:基类的构造(析构)函数被调用了多次。
- 编译问题:调用基类的成员函数会产生二义性。
现在,终于轮到虚拟继承出场了!虚拟继承其实很简单,就是在基类前面加上一个virtual关键字。修改之后的代码如下:
#include <iostream>using namespace std;class A
{public:
A(){cout<<"This is A constructor."<<endl;}
~A(){cout<<"This is A destructor."<<endl;}
void speak(){cout<<"This is A speak!"<<endl;}};class B1: public virtual A{public:
B1(){cout<<"This is B1 constructor."<<endl;}
~B1(){cout<<"This is B1 destructor."<<endl;}
};class B2: virtual public A{public:
B2(){cout<<"This is B2 constructor."<<endl;}
~B2(){cout<<"This is B2 destructor."<<endl;}
};class C:public B1,public B2{public:
C(){cout<<"This is C constructor."<<endl;}
~C(){cout<<"This is C destructor."<<endl;}
};int main()
{C *c=new C();
c->speak();delete c;
return 0;
}
注意关键字virtual的位置。这时我们再运行程序,得到的结果如下:
看到了吧,class A的构造函数只执行了一遍,而且speak()函数正常执行,没有产生二义性。忘了说一句,被虚拟继承的基类就是虚基类,例如上面程序中的class A。
今天虚拟继承和虚基类就总结到这,如有疑问,欢迎评论!
感谢:《C++编程关键路径——程序员求职指南》
- c++-虚拟继承、虚基类
- C++之虚拟继承和虚基类
- C++:菱形继承和虚拟继承
- 虚拟继承和虚基类
- C++: 虚表和菱形虚拟继承
- (虚拟继承)Problem C: 学生干部虚基类
- 虚拟函数和虚拟继承
- C++-继承:多重继承 && 虚拟继承
- c++之虚拟继承
- 多重继承和虚拟继承
- 虚拟继承和模板继承
- 【c++】菱形虚拟虚拟继承模型探索
- C++专题总结之理解虚拟函数、多继承、虚基类和RTTI
- 虚拟继承,虚基类
- 虚拟继承,虚基类
- 虚拟继承,虚基类
- 虚拟继承、虚基类
- 虚拟继承与虚基类
- C++之类的继承
- Spring ------ 注解详解
- 微信小程序开发:e代驾Lite小程序分析
- C++之虚函数
- 通过配置的容器过滤
- C++之虚拟继承和虚基类
- Css/html兼容性问题
- VMware workstation+Ubuntu 16.04 英文版设置中文界面以及设置中文输入法
- listener 定时器TimerTask
- C++之STL概述
- Spark学习——缓存、闭包及共享变量
- 老梁说天下——慈善的红与黑
- rest路径形式相同的话,通过@GET @DELETE 区别
- poj 1848 Tree 树形dp