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++虚继承的概念[转]的更多相关文章

  1. C++虚继承的概念(转)

    http://blog.csdn.net/wangxingbao4227/article/details/6772579 C++中虚拟继承的概念 为了解决从不同途径继承来的同名的数据成员在内存中有不同 ...

  2. C++中的继承与虚函数各种概念

    虚继承与一般继承 虚继承和一般的继承不同,一般的继承,在目前大多数的C++编译器实现的对象模型中,派生类对象会直接包含基类对象的字段.而虚继承的情况,派生类对象不会直接包含基类对象的字段,而是通过一个 ...

  3. Java基础 之软引用、弱引用、虚引用 ·[转载]

    Java基础 之软引用.弱引用.虚引用 ·[转载] 2011-11-24 14:43:41 Java基础 之软引用.弱引用.虚引用 浏览(509)|评论(1)   交流分类:Java|笔记分类: Ja ...

  4. C++对象模型:单继承,多继承,虚继承

    什么是对象模型 有两个概念可以解释C++对象模型: 语言中直接支持面向对象程序设计的部分.对于各种支持的底层实现机制. 类中成员分类 数据成员分为静态和非静态,成员函数有静态非静态以及虚函数 clas ...

  5. C++构造函数 & 拷贝构造函数 & 派生类的构造函数 & 虚继承的构造函数

    构造函数 ,是一种特殊的方法 .主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中 .特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数 ...

  6. C++学习之虚继承

    http://blog.csdn.net/wangxingbao4227/article/details/6772579 C++中虚拟继承的概念 为了解决从不同途径继承来的同名的数据成员在内存中有不同 ...

  7. C++ 深入理解 虚继承、多重继承和直接继承

    [摘要] 本文从5段代码实例出发,通过类中类的普通继承,类的虚继承,类的多重继承,多个虚函数类的普通继承.虚继承与多重继承,几个交叉概念,详细的阐释了继承.虚函数与虚继承的基本概念,深入剖析了继承于虚 ...

  8. c++虚继承与虚函数

    学习继承与多态时看到这两个概念,记录整理. 虚继承与虚函数都是用virtual关键字实现,虚继承为了防止多重继承,而虚函数为了实现多态. 是几个例子. 虚继承: class A{}; class B: ...

  9. 虚函数&纯虚函数&抽象类&虚继承

    C++ 虚函数&纯虚函数&抽象类&接口&虚基类   1. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过 ...

随机推荐

  1. Java并发(二)多线程的好处

    即使编写多线程程序具有挑战性,但它仍在使用中,是因为它可以带来如下的好处: 更好的资源利用 在某些场景下程序的设计会更简单 提升程序的响应性 更好的资源利用 假设一个应用会从本地文件系统中读取和处理文 ...

  2. linux异步通知

    简述 linux下异步方式有两种:异步通知和异步IO(AIO),aio请参考:linux异步IO--aio 异步通知的含义是:一旦设备就绪,则主动通知应用程序,这样应用程序就不需要查询设备状态,准确称 ...

  3. excel导出功能优化

    先说说优化前,怎么做EXCEL导出功能的: 1. 先定义一个VO类,类中的字段按照EXCEL的顺序定义,并且该类只能用于EXCEL导出使用,不能随便修改. 2. 将查询到的结果集循环写入到这个VO类中 ...

  4. RTMP规范 消息与消息块

    Real Time Messaging Protocol(实时消息传输协议) 应用层协议 RTMP协议中, 基本数据单元称为消息(Message).当RTMP协议在互联网中传输数据的时候,消息会被拆分 ...

  5. 关于PHP的特点

    魔术方法 当一个对象引用变量调用一个没有定义的属性或方法时,可以这一个函数.当发生这种情况时调用这种函数.

  6. CString常用函数

    转自:http://www.cnblogs.com/Caiqinghua/archive/2009/02/16/1391190.html CString( );例:CString csStr; CSt ...

  7. linux -- at命令

    在windows系统中,windows提供了计划任务这一功能,在控制面板 -> 性能与维护 -> 任务计划, 它的功能就是安排自动运行的任务. 通过'添加任务计划'的一步步引导,则可建立一 ...

  8. linux -- Ubuntu Server 安装图形界面

    1.连接网络,你一定要确保网络通畅,如果你和我一样使用Wireless,那先找根网线插上,因为下面的安装都要通过网络下载组件的. 2.进入图形界面的命令是startX,敲击后会有安装xinit的提示. ...

  9. 云计算中auto-scaling 最早的来源

    什么是弹性?首先,整合计算资源,将计算资源池化,通过虚拟机按需使用计算资源;其次,按量计费,让用户能够根据使用量按月按时甚至按秒来进行付费. 不过,光有了这两条还不够.为什么?我举个例子: 很多做运维 ...

  10. 监视在input框中按下回车(enter) js实现

    function getKey(event) { if (event.keyCode == 13) { alert("我是回车键"); } } <input type=&qu ...