虚拟基类是为解决多重继承而出现的。
 
以下面的一个例子为例:
#include <iostream.h>
#include <memory.h>
class CA
{
int k; //如果基类没有数据成员,则在这里多重继承编译不会出现二义性
public:
void f() {cout << "CA::f" << endl;}
};
class CB : public CA
{
};
class CC : public CA
{
};
class CD : public CB, public CC
{
};
void main()
{
CD d;
d.f();
}
当编译上述代码时,我们会收到如下的错误提示:
error C2385: 'CD::f' is ambiguous
即编译器无法确定你在d.f()中要调用的函数f到底是哪一个。这里可能会让人觉得有些奇怪,命名只定义了一个CA::f,既然大家都派生自CA,那自然就是调用的CA::f,为什么还无法确定呢?
这是因为编译器在进行编译的时候,需要确定子类的函数定义,如CA::f是确定的,那么在编译CB、CC时还需要在编译器的语法树中生成CB::f,CC::f等标识,那么,在编译CD的时候,由于CB、CC都有一个函数f,此时,编译器将试图生成这两个CD::f标识,显然这时就要报错了。(当我们不使用CD::f的时候,以上标识都不会生成,所以,如果去掉d.f()一句,程序将顺利通过编译)
要解决这个问题,有两个方法:
1、重写函数f():此时由于我们明确定义了CD::f,编译器检查到CD::f()调用时就无需再像上面一样去逐级生成CD::f标识了;
此时CD的元素结构如下:
|CB(CA)|
|CC(CA)|
故此时的sizeof(CD) = 8;(CB、CC各有一个元素k)
2、使用虚拟继承:虚拟继承又称作共享继承,这种共享其实也是编译期间实现的,当使用虚拟继承时,上面的程序将变成下面的形式:
#include <iostream.h>
#include <memory.h>
class CA
{
int k;
public:
void f() {cout << "CA::f" << endl;}
};
class CB : virtual public CA //也有一种写法是class CB : public virtual CA
{ //实际上这两种方法都可以
};
class CC : virtual public CA
{
};
class CD : public CB, public CC
{
};
void main()
{
CD d;
d.f();
}
此时,当编译器确定d.f()调用的具体含义时,将生成如下的CD结构:
|CB|
|CC|
|CA|
同时,在CB、CC中都分别包含了一个指向CA的虚基类指针列表vbptr(virtual base table pointer)(虚基表指针),其中记录的是从CB、CC的vbtable的首地址(vbptr)到CA的元素之间的偏移量。此时,不会生成各子类的函数f标识,除非子类重写了该函数,从而达到“共享”的目的(这里的具体内存布局,可以参看钻石型继承内存布局,在白杨的那篇文章中也有)(VS2010中,在Project Properties->C++->Command Line->Additional Options里面加上/d1reportSingleClassLayoutX,可以查看类X的对象布局)。
也正因此,此时的sizeof(CD) = 12(vbptrCB + vbptrCC + sizoef(int))(32位机中指针占4个字节);
另注:
如果CB,CC中各定义一个int型变量,则sizeof(CD)就变成20(两个vbptr + 3个sizoef(int)
如果CA中添加一个virtual void f1(){},sizeof(CD) = 16(vfptrCA +vbptrCB + vbptrCC + sizoef(int));
再添加virtual void f2(){},sizeof(CD) = 16不变。原因如下所示:带有虚函数(大于等于1个)的类,其内存布局上包含一个指向虚函数列表的指针(vfptr)(虚函数表指针),这跟该类有几个虚函数无关。
 
 
引用:
1. 虚继承_百度百科  http://baike.baidu.com/link?url=Q-BGdH5Cs5CtDRti1VaDPOI5ws1pVK4INmwGXp4NYrUWQ4xSMdgTu5CAmEBg9YMeA-Npeg3QiOthoIpSmVmlyK#2_1
 
实例:
1. 多继承的困惑:
#include <iostream.h>
#include <memory.h> #if 1
#define VIRTUAL virtual
#else
#define VIRTUAL
#endif class CA
{
int k; //如果基类没有数据成员,则在这里多重继承编译不会出现二义性
public:
VIRTUAL void f() {cout << "CA::f" << endl;} //不论基类的方法是否是虚函数,子类继承后都会有他的实例。
};
class CB : public CA
{
};
class CC : public CA
{
};
class CD : public CB, public CC
{
}; int main(void)
{
CD d;
d.f();
return ;
}
/*
test.cpp:29: error: request for member ‘f’ is ambiguous
*/

c++,为什么要引入虚拟继承的更多相关文章

  1. C++ 虚拟继承

    1.为什么要引入虚拟继承 虚拟继承是多重继承中特有的概念.虚拟基类是为解决多重继承而出现的.如:类D继承自类B1.B2,而类B1.B2都继 承自类A,因此在类D中两次出现类A中的变量和函数.为了节省内 ...

  2. 关于C++中的虚拟继承的一些总结

    1.为什么要引入虚拟继承 虚拟继承是多重继承中特有的概念.虚拟基类是为解决多重继承而出现的.如:类D继承自类B1.B2,而类B1.B2都继承自类A,因此在类D中两次出现类A中的变量和函数.为了节省内存 ...

  3. 图文例解C++类的多重继承与虚拟继承

    文章导读:C++允许为一个派生类指定多个基类,这样的继承结构被称做多重继承. 在过去的学习中,我们始终接触的单个类的继承,但是在现实生活中,一些新事物往往会拥有两个或者两个以上事物的属性,为了解决这个 ...

  4. 浅析GCC下C++多重继承 & 虚拟继承的对象内存布局

    继承是C++作为OOD程序设计语言的三大特征(封装,继承,多态)之一,单一非多态继承是比较好理解的,本文主要讲解GCC环境下的多重继承和虚拟继承的对象内存布局. 一.多重继承 先看几个类的定义: 01 ...

  5. 《挑战30天C++入门极限》图文例解C++类的多重继承与虚拟继承

        图文例解C++类的多重继承与虚拟继承 在过去的学习中,我们始终接触的单个类的继承,但是在现实生活中,一些新事物往往会拥有两个或者两个以上事物的属性,为了解决这个问题,C++引入了多重继承的概念 ...

  6. c++面试常用知识(sizeof计算类的大小,虚拟继承,重载,隐藏,覆盖)

    一. sizeof计算结构体 注:本机机器字长为64位 1.最普通的类和普通的继承 #include<iostream> using namespace std; class Parent ...

  7. 继承虚函数浅谈 c++ 类,继承类,有虚函数的类,虚拟继承的类的内存布局,使用vs2010打印布局结果。

    本文笔者在青岛逛街的时候突然想到的...最近就有想写几篇关于继承虚函数的笔记,所以回家到之后就奋笔疾书的写出来发布了 应用sizeof函数求类巨细这个问题在很多面试,口试题中很轻易考,而涉及到类的时候 ...

  8. 关于虚拟继承类的大小问题探索,VC++ 和 G++ 结果是有区别的

    昨天笔试遇到个 关于类占用的空间大小的问题,以前没怎么重视,回来做个试验,还真发现了问题,以后各位笔试考官门,出题时请注明是用什么编译器. vc6/vc8 cl 和 Dev-C 的g++ 来做的测试: ...

  9. C++多重继承与虚拟继承

    本文只是粗浅讨论一下C++中的多重继承和虚拟继承. 多重继承中的构造函数和析构函数调用次序 我们先来看一下简单的例子: #include <iostream> using namespac ...

随机推荐

  1. Android 常用开源代码整理

    1.AndroidAnnotations一个强大的android开源注解框架, 基本上可以注入任何类型, 比一般的所谓的注入框架要快, 因为他是通过生成一个子类来实现的绑定.具体查看文档. 2.and ...

  2. Datamatrix码

    DataMatrix二维条码原名Datacode,由美国国际资料公司(International Data Matrix, 简称ID Matrix)於1989年发明.DataMatrix二维条码是一种 ...

  3. UNIX 缩写风格

    构建于图形界面之上的操作系统,使用鼠标作为主输入设备, 是否使用缩写并不重要.比如 Windows 系统中的目录,几乎都是全称…… 点击两次鼠标进入文件夹 pf, 并不意味着点击13次才能进入文件夹  ...

  4. cocos2d-x游戏开发系列教程-超级玛丽07-CMGameMap(五)-地图卷动

    马里奥在平移的过程中,涉及到地图的卷动问题. 在这个游戏里,地图比窗口大,窗口只是显示了地图的一部分,因此马里奥在移动的时候,移动到一定位置之后要卷动地图,否则马里奥移动到窗口右边之后......那结 ...

  5. cocos2dx进阶学习之坐标转换

    在cocos2dx中,有四种坐标系 GL坐标系:左下为原点,x轴向右,y轴向上 UI坐标系:左上为原点,x轴向右,y轴向下 世界坐标系:与GL坐标系相同 本地坐标系:是节点(CCNode)的坐标系,原 ...

  6. 初遇Git与MarkDown 文件

    新年好! 昨晚熬夜在学Git,稍微会了一些命令. 推荐大家去try.github.io上学习,这是GitHub提供的网页,它在网页提供了一个“伪”模拟器,根据网页的提示学习命令.网页上说15分钟就能学 ...

  7. UITabbar的常用属性

    // //设置tabbar的背景颜色 // [self.tabBar setBarTintColor:[UIColor redColor]]; // //设置选中时图片和文字的颜色 // [self. ...

  8. SQL Server 自学笔记

    --★★★SQL语句本身区分大小写吗 --SQLServer 不区分大小写 --Oracle 默认是区分大小写的 --datetime的输入格式,2008-01-07输入进去后显示为1905-06-2 ...

  9. javascript面向对象——继承

    javascript和其他语言相比,它没有真正意义上的继承,也不能从一个父类extends,要实现它的继承可以通过其他方式来实现: 步骤:1.继承父类的属性 2.继承父类的原型 下面就以一个拖拽为例子 ...

  10. ASP.net 学习路线(详细)

    .net学习路线 入门篇1.         学习面向对象(OOP)的编程思想 许多高级语言都是面向对象的编程,.NET也不例外.如果您第一次接触面向对象的编程,就必须理解类.对象.字段.属性.方法和 ...