C++虚继承的概念[转]
C++中虚拟继承的概念
为了解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。这样不仅就解决了二义性问题,也节省了内存,避免了数据不一致的问题。
class 派生类名:virtual 继承方式 基类名
virtual是关键字,声明该基类为派生类的虚基类。
在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。
声明了虚基类之后,虚基类在进一步派生过程中始终和派生类一起,维护同一个基类子对象的拷贝。
C++虚拟继承
◇概念:
C++使用虚拟继承(Virtual Inheritance),解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。
◇解决问题:
解决了二义性问题,也节省了内存,避免了数据不一致的问题。
◇同义词:
虚基类(把一个动词当成一个名词而已)
当在多条继承路径上有一个公共的基类,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个公共基类说明为虚基类。
◇语法:
class 派生类: virtual 基类1,virtual 基类2,...,virtual 基类n
{
...//派生类成员声明
};
◇执行顺序
首先执行虚基类的构造函数,多个虚基类的构造函数按照被继承的顺序构造;
执行基类的构造函数,多个基类的构造函数按照被继承的顺序构造;
执行成员对象的构造函数,多个成员对象的构造函数按照申明的顺序构造;
执行派生类自己的构造函数;
析构以与构造相反的顺序执行;
mark
从虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类的构造函数的调用在执行中被忽略,从而保证对虚基类子对象只初始化一次。
在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。
◇因果:
多重继承->二义性->虚拟继承解决
◇二义性:
1: //-----------------------------------------------------
2: //名称:blog_virtual_inherit.cpp
3: //说明:C++虚拟继承学习演示
4: //环境:VS2005
5: //blog:pppboy.blog.163.com
6: //----------------------------------------------------
7: #include "stdafx.h"
8: #include <iostream>
9: using namespace std;
10:
11: //Base
12: class Base
13: {
14: public:
15: Base(){cout << "Base called..."<< endl;}
16: void print(){cout << "Base print..." <<endl;}
17: private:
18: };
19:
20: //Sub
21: class Sub //定义一个类 Sub
22: {
23: public:
24: Sub(){cout << "Sub called..." << endl;}
25: void print(){cout << "Sub print..." << endl;}
26: private:
27: };
28:
29: //Child
30: class Child : public Base , public Sub //定义一个类Child 分别继承自 Base ,Sub
31: {
32: public:
33: Child(){cout << "Child called..." << endl;}
34: private:
35: };
36:
37: int main(int argc, char* argv[])
38: {
39: Child c;
40:
41: //不能这样使用,会产生二意性,VC下error C2385
42: //c.print();
43:
44: //只能这样使用
45: c.Base::print();
46: c.Sub::print();
47:
48: system("pause");
49: return 0;
50: }
◇多重继承:
1: //-----------------------------------------------------
2: //名称:blog_virtual_inherit.cpp
3: //说明:C++虚拟继承学习演示
4: //环境:VS2005
5: //blog:pppboy.blog.163.com
6: //----------------------------------------------------
7: #include "stdafx.h"
8: #include <iostream>
9: using namespace std;
10:
11: int gFlag = 0;
12:
13: class Base
14: {
15: public:
16: Base(){cout << "Base called : " << gFlag++ << endl;}
17: void print(){cout << "Base print" <<endl;}
18: };
19:
20: class Mid1 : public Base
21: {
22: public:
23: Mid1(){cout << "Mid1 called" << endl;}
24: private:
25: };
26:
27: class Mid2 : public Base
28: {
29: public:
30: Mid2(){cout << "Mid2 called" << endl;}
31: };
32:
33: class Child:public Mid1, public Mid2
34: {
35: public:
36: Child(){cout << "Child called" << endl;}
37: };
38:
39: int main(int argc, char* argv[])
40: {
41: Child d;
42:
43: //不能这样使用,会产生二意性
//d.print();
45:
46: //只能这样使用
47: d.Mid1::print();
48: d.Mid2::print();
49:
50: system("pause");
51: return 0;
52: }
53:
//output
Base called : 0
Mid1 called
Base called : 1
Mid2 called
Child called
Base print
Base print
◇虚拟继承
在派生类继承基类时,加上一个virtual关键词则为虚拟继承
1: //-----------------------------------------------------
2: //名称:blog_virtual_inherit.cpp
3: //说明:C++虚拟继承学习演示
4: //环境:VS2005
5: //blog:pppboy.blog.163.com
6: //----------------------------------------------------
7: #include "stdafx.h"
8: #include <iostream>
9: using namespace std;
10:
11: int gFlag = 0;
12:
13: class Base
14: {
15: public:
16: Base(){cout << "Base called : " << gFlag++ << endl;}
17: void print(){cout << "Base print" <<endl;}
18: };
19:
20: class Mid1 : virtual public Base
21: {
22: public:
23: Mid1(){cout << "Mid1 called" << endl;}
24: private:
25: };
26:
27: class Mid2 : virtual public Base
28: {
29: public:
30: Mid2(){cout << "Mid2 called" << endl;}
31: };
32:
33: class Child:public Mid1, public Mid2
34: {
35: public:
36: Child(){cout << "Child called" << endl;}
37: };
38:
39: int main(int argc, char* argv[])
40: {
41: Child d;
42:
43: //这里可以这样使用
44: d.print();
45:
46: //也可以这样使用
47: d.Mid1::print();
48: d.Mid2::print();
49:
50: system("pause");
51: return 0;
52: }
53:
//output
1: Base called : 0
2: Mid1 called
3: Mid2 called
4: Child called
5: Base print
6: Base print
7: Base print
8: 请按任意键继续. . .
◇通过输出的比较
1.在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。
2.声明了虚基类之后,虚基类在进一步派生过程中始终和派生类一起,维护同一个基类子对象的拷贝。
3.观察类构造函数的构造顺序,拷贝也只有一份。
◇与虚函数关系
虚拟继承与虚函数有一定相似的地方,但他们之间是绝对没有任何联系的。
再想一次:虚拟继承,虚基类,虚函数。
C++虚继承的概念[转]的更多相关文章
- C++虚继承的概念(转)
http://blog.csdn.net/wangxingbao4227/article/details/6772579 C++中虚拟继承的概念 为了解决从不同途径继承来的同名的数据成员在内存中有不同 ...
- C++中的继承与虚函数各种概念
虚继承与一般继承 虚继承和一般的继承不同,一般的继承,在目前大多数的C++编译器实现的对象模型中,派生类对象会直接包含基类对象的字段.而虚继承的情况,派生类对象不会直接包含基类对象的字段,而是通过一个 ...
- Java基础 之软引用、弱引用、虚引用 ·[转载]
Java基础 之软引用.弱引用.虚引用 ·[转载] 2011-11-24 14:43:41 Java基础 之软引用.弱引用.虚引用 浏览(509)|评论(1) 交流分类:Java|笔记分类: Ja ...
- C++对象模型:单继承,多继承,虚继承
什么是对象模型 有两个概念可以解释C++对象模型: 语言中直接支持面向对象程序设计的部分.对于各种支持的底层实现机制. 类中成员分类 数据成员分为静态和非静态,成员函数有静态非静态以及虚函数 clas ...
- C++构造函数 & 拷贝构造函数 & 派生类的构造函数 & 虚继承的构造函数
构造函数 ,是一种特殊的方法 .主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中 .特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数 ...
- C++学习之虚继承
http://blog.csdn.net/wangxingbao4227/article/details/6772579 C++中虚拟继承的概念 为了解决从不同途径继承来的同名的数据成员在内存中有不同 ...
- C++ 深入理解 虚继承、多重继承和直接继承
[摘要] 本文从5段代码实例出发,通过类中类的普通继承,类的虚继承,类的多重继承,多个虚函数类的普通继承.虚继承与多重继承,几个交叉概念,详细的阐释了继承.虚函数与虚继承的基本概念,深入剖析了继承于虚 ...
- c++虚继承与虚函数
学习继承与多态时看到这两个概念,记录整理. 虚继承与虚函数都是用virtual关键字实现,虚继承为了防止多重继承,而虚函数为了实现多态. 是几个例子. 虚继承: class A{}; class B: ...
- 虚函数&纯虚函数&抽象类&虚继承
C++ 虚函数&纯虚函数&抽象类&接口&虚基类 1. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过 ...
随机推荐
- git学习(三):git暂存区
回顾之前学过的命令: git init // 初始化一个项目 git add // 将文件交给工作区 git commit // 提交修改 查看提交日志: git log // 查看提交日志 git ...
- C语言 · 第二大整数
算法提高 第二大整数 时间限制:1.0s 内存限制:512.0MB 问题描述 编写一个程序,读入一组整数(不超过20个),当用户输入0时,表示输入结束.然后程序将从这组整数中,把第二 ...
- size_t ssize_t socklen_t
size_t 解释一:为了增强程序的可移植性,便有了size_t,它是为了方便系统之间的移植而定义的,不同的系统上,定义size_t可能不一样. 在32位系统上 定义为 unsigned int 也就 ...
- 解决Spring框架的Dao层改用@Repository注解,无法使用JdbcDaoSupport的问题
解决Spring框架的Dao层改用@Repository注解,无法使用JdbcDaoSupport的问题 Alternatively, create an own implementation of ...
- 手工配置oracle数据库
手工配置Oracle 10G Enterprise Manager今天安装oracle,反复装了几遍都报下面错误:试了几种方法都不行:由于以下错误,Enterprise Manager配置失败启动Da ...
- 使用特殊构造的5GB文件测试Win2012Dedup功能
WinServer2012一个崭新的功能就是支持重复数据删除功能.为了测试这个功能的情况,特别构造了一个特殊的5GB文件用于此次测试.惯例,先说下测试环境的搭建1 使用ESXi5上的版本8的VM,安装 ...
- (转)音频输出PCM与LPCM有什么不同
多声道LPCM:无损音轨原始存在格式,概念上等效于wave文件,并不需要运算解码,可直接输入功放进行DA转换,光纤和同轴接口只能传输2声道LPCM,多声道LPCM需要HDMI接口传输. PCM: ...
- Ubuntu telnet
首先在Ubuntu中安装xinetd(它是inetd替代品): sudo apt-get install xinetd 再安装telnetd,在Ubuntu中没有telnetd这个软件包,它是包含在i ...
- 最短路径问题-Floyd算法
概念 最短路径也是图的一个应用,即寻找图中某两个顶点的最短路径长度. 实际应用:例如确定某两个城市间的坐火车最短行车路线长度等. Floyd algorithm 中文名就是弗洛伊德算法. 算法思路:用 ...
- C# 使用IrisSkin2.dll皮肤库C# ssk皮肤
其实皮肤就是一个第三方的控件,名字是IrisSkin2.dll只要添加到你的工具箱里就可以和其它控件一样使用了下面我说一下使用的方法,不对的地方大家多指教啊. 一.添加控件IrisSkin2.dll. ...