虚拟基类是为解决多重继承而出现的。
以下面的一个例子为例:
#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
#endifclass 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 0;
}
/*
test.cpp:29: error: request for member ‘f’ is ambiguous
*/

c++,为什么要引入虚拟继承相关推荐

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

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

  2. C++随笔——虚拟继承

    虚拟继承 C++使用虚拟继承(Virtual Inheritance),使得派生类如果继承基类多次,但只有一份基类的拷贝在派生类对象中. 虚拟继承的语法: class 派生类: virtual 基类1 ...

  3. C++对象内存布局--⑤GCC编译器--单个虚拟继承

    C++对象内存布局--⑤GCC编译器--单个虚拟继承 测试GNU的GCC编译器在处理虚拟继承上跟VS不同的地方.派生类的虚函数表跟虚基类表合并. //GCC编译器--单个虚拟继承.cpp //2010 ...

  4. [YTU]_2622(B 虚拟继承(虚基类)-沙发床(改错题))

    题目描述 有一种特殊的床,既能当床(Bed)用又能当沙发(Sofa)用,所以叫沙发床(SleeperSofa). 同时床和沙发又是一种特殊的家具(Furniture),具有一切家具的特性. 利用虚拟继 ...

  5. 菱形继承与菱形虚拟继承

    菱形继承 单继承:一个子类只有一个直接父类时称这个继承关系为单继承 多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承 菱形继承:菱形继承是多继承的一种特殊情况. 来看看下面这段菱形继承的代 ...

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

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

  7. C++虚继承(五) --- 虚拟继承的概念

    C++中虚拟继承的概念 为了解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类.这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数 ...

  8. C++对象内存布局--④VS编译器--单个虚拟继承

    C++对象内存布局--④VS编译器--单个虚拟继承 在VS2005编译器下,证明单个虚拟继承的内存布局:无论有无虚函数,必然含有虚基类表指针.虚基类表中的内容为本类实例的偏移和基类实例的相对偏移值. ...

  9. c++的虚拟继承 的一些思考吧

    虚拟继承是多重继承中特有的概念.虚拟基类是为解决多重继承而出现的.如:类D继承自类B1.B2,而类B1.B2都继承自类A,因此在类D中两次出现类A中的变量和函数.为了节省内存空间,可以将B1.B2对A ...

最新文章

  1. 第八章 泛型程序设计
  2. jeecg富文本编辑器增加字体(仿宋)
  3. 《大数据的“道”“术”“释”》----读书摘录+思考
  4. argparse.ArgumentParser
  5. 理解hasOwnProperty()的作用
  6. 程序的内存模型—new运算符
  7. VTK:Shaders之MarbleShaderDemo
  8. Qt Creator创建Qt Quick项目
  9. 插值查找+代码实现+注意事项
  10. 长春南关区净月大街附近都有哪些课后班?
  11. hpux 下查看内存的的大小的几种方法:
  12. maven打包时把依赖的jar包打进去
  13. M1 Mac用户:如何检查SSD是否过度磨损
  14. iOS开发之网络编程--使用NSURLConnection实现大文件断点续传下载+使用输出流代替文件句柄...
  15. Java连接数据库访问失败
  16. Intel ICH9 sata驱动
  17. 手把手教你开发红外遥控器
  18. 易宝典——玩转O365中的EXO服务 之五十 如何知道微软管理员进行了哪些操作
  19. bootstrap--表格(table的各种样式)
  20. 如何将“\”替换为任意字符

热门文章

  1. phpcms 指定id范围 调用_Dubbogo 源码笔记(二)客户端调用过程
  2. distinct性能问题_Mysql性能优化:如何给字符串加索引?
  3. python自动化上传文件_python接口自动化测试二十三:文件上传
  4. vscode中控制台不能输入_vscode控制台不能输入怎么办
  5. 用户视图切换为Linux视图,视图_ITPUB博客
  6. python dict setdefault_Python dict setdefault()用法及代码示例
  7. 依赖注入和控制反转的理解
  8. python爬取一条新闻内容_自己做语料——Python爬取新闻联播文字版
  9. 程序设计教程用c 语言编程,程序设计教程--用C 语言编程
  10. 取消button的点击效果_(Vue动效)6.Vue中列表过渡效果