虚基类的概念及用法

如果派生类的全部或者部分基类有共同的基类,那么派生类的这些直接基类从上一级基类继承的成员都具有相同的名称,定义了派生类的对象后,同名数据成员就会在内存中有多份拷贝,同名函数也会有多个映射。

访问这些同名成员时,为了唯一标识它们可以使用上一讲中的作用域分辨符,也可以使用虚基类技术。

我们将派生类直接基类的共同基类声明为虚基类后,派生类从不同的直接基类继承来的同名数据成员在内存中就会只有一份拷贝,同名函数也会只有一个映射,这样不仅实现了唯一标识同名成员,而且也节省了内存空间,可见虚基类技术是很实用的。

在派生类声明时除继承方式外还使用关键字virtual限定基类的话,此基类就是虚基类。虚基类声明的语法形式为:

class 派生类名:virtual 继承方式 基类名

这里关键字virtual跟继承方式一样,只限定紧跟在它后面的基类。

比如,声明了类A为虚基类,类B为A的派生类,类C也是A的派生类,类D是由类B和C共同继承而来,则类B和类C从A继承的同名数据成员在类D的对象中只有一份拷贝,同名函数成员也只有一个函数体。

讲上一讲中的第二个例子做下修改,将Base0声明为虚基类来说明下虚基类的用法:我们先声明一个基类Base0,Base0中有数据成员x和函数成员show,再声明类Base1和Base2,它们都由Base0公有继承而来,与上一讲中不同的是派生时声明Base0为虚基类,最后从Base1和Base2共同派生出类Child。

这时Base0的成员经过到Base1和Base2再到Child的两次派生过程,出现在Child类中时,数据成员x在内存中也只有一份拷贝,函数成员show也只有一个映射。

#include <iostream>
using namespace std;
class Base0                  // 基类Base0的声明
{public:int x;void show() { cout<<"x of Base0: "<<x<<endl; }
};
class Base1 : virtual public Base0    // Base0为虚基类,公有派生Base1类
{};
class Base2 : virtual public Base0    // Base0为虚基类,公有派生Base2类
{};
class Child : public Base1, public Base2
{};
int main()
{Child child;child.x = 5;child.show();return 0;
}


大家可以看到,声明虚基类只需要在它的派生类声明时使用关键字virtual修饰。

我们对作用域分辨符和虚基类技术进行对比分析可知,使用作用域分辨符唯一标识同名成员时,派生类中有同名成员的多个拷贝,可以存放不同的数据,进行不同的操作,而使用虚基类时派生类的同名成员只有一份拷贝,更节省内存。

我们在软件开发中可以根据实际情况自己做出选择。

虚基类派生类的构造函数

上面例子中各个类都没有定义构造函数,而是使用的默认构造函数。

如果虚基类定义了带参数表的非默认构造函数,没有定义默认形式的构造函数,那么情况会有些复杂。因为由虚基类直接或间接继承的所有派生类,都必须在构造函数的成员初始化列表中给出对虚基类成员的初始化。

这里再讲上面的例子做进一步修改,为虚基类添加带参数表的构造函数,那么整个程序就要改成以下形式:

#include <iostream>
using namespace std;
class Base0                    // 基类Base0的声明
{public:Base0(int y)     { x=y; }int x;void show()      { cout<<"x of Base0: "<<x<<endl; }
};
class Base1 : virtual public Base0     // Base0为虚基类,公有派生Base1类
{public:Base1(int y):Base0(y)    { }
};
class Base2 : virtual public Base0     // Base0为虚基类,公有派生Base2类
{public:Base2(int y):Base0(y)    { }
};
class Child : public Base1, public Base2
{public:Child(int y):Base0(y),Base1(y),Base2(y)   { }
};
int main()
{Child child(3);child.show();return 0;
}


主函数中定义了派生类Child的对象child,在构造对象child时调用了child的构造函数,其初始化列表中不只调用了虚基类Base0的构造函数对从它继承的成员x进行初始化,而且还调用了基类Base1和Base2的构造函数Base1()和Base2(),而Base1()和Base2()的初始化列表中又有对虚基类Base0成员x的初始化。

这么说,从虚基类Base0继承来的成员x初始化了三次,其实不然,因为编译器在遇到这种情况时会进行特殊处理:如果构造的对象中有从虚基类继承来的成员,那么虚基类成员的初始化由而且只由最远派生类的构造函数调用虚基类的构造函数来完成。

最远派生类就是声明对象时指定的类,上面例子中构造对象child时,类Child就是最远派生类。

除了最远派生类,它的其他基类对虚基类构造函数的调用会被忽略。

上例中就只会由Child类的构造函数调用虚基类Base0的构造函数完成成员x的初始化,而Child类的基类Base1和Base2对虚基类Base0构造函数的调用会被忽略。

虚基类及其派生类的构造函数相关推荐

  1. 构造函数怎么在主函数调用_C++ 虚基类及其派生类构造函数(学习笔记:第7章 12)...

    虚基类及其派生类构造函数[1] 建立对象时所指定的类称为最远派生类. 虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的. 在整个继承结构中,直接或间接继承虚基类的所有派生类,都 ...

  2. C++中基类与派生类的构造函数和析构函数

    1.Cpp中的基类与派生类的构造函数 基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承.构造函数不能被继承是有道理的,因为即使继承了,它的名字和 ...

  3. 基类、派生类、虚基类、虚函数、虚析构、纯虚函数、抽象类

    基类:被其它类通过继承方式作为父类继承的类称为基类:描述派生类的统一种类的方式. 派生类:通过继承其他类(并可能含有自定义成员)实现的子类:为提高代码的重用性及与同样继承于同一个父类的其它类形成统一种 ...

  4. 深剖基类和派生类的虚函数表

    1.当派生类实现基类的虚函数时,基类中虚函数表和虚函数地址和派生类中虚函数表和虚函数地址不同: 当派生类不实现基类的虚函数时,基类中虚函数表和虚函数地址和派生类中虚函数表和虚函数的地址相同. 1.派生 ...

  5. c++ 基类和派生类的虚函数表是否为同一个

    总结 派生类实现基类的虚函数时,基类中虚函数表和派生类的虚函数表地址不同,基类虚函数表中的虚函数地址和派生类虚函数表中的虚函数地址不同: 派生类不实现基类的虚函数时,基类中虚函数表和派生类中虚函数表地 ...

  6. 派生类到基类的转换 和基类到派生类的转换

    一. 基类与派生类的转换     3种继承方式(公用.保护.私有继承)中,公用派生类才是基类真正的子类型,它完整地继承了基类的功能.     不同类型数据之间在一定条件下可以进行类型的转换.基类与派生 ...

  7. C++学习 十五、类继承(1)基类,派生类,访问权限,protected

    C++学习 十五.类继承(1)基类,派生类 前言 类继承 类的关系与继承 基类, 派生类 基类 派生类 构造函数,析构函数 文件位置 访问权限 protected 后记 前言 本篇开始学习C++类的继 ...

  8. 14.11 基类与派生类关系的详细再探讨

    一:派生类对象模型简述 Men mymen:子类(派生类对象),包含多个组成部分(也就是多个子对象); <1>一个是含有派生类自己定义的成员变量,成员函数的子对象: <2>一个 ...

  9. C++ 抽象基类与派生类

    抽象基类与派生类:构造方法的注意事项 1.抽象基类的构造方法不要用纯虚函数 2.子类的属性名如果和抽象基类的属性名相同的情况下 (1).使用子类对象调用此属性时会优先调用子类的属性 (2).在构造方法 ...

  10. C++:基类和派生类

    4.1 派生类的声明 继承实例如下: class Person{ //声明基类Person public:void print(){cout<<"name:"<& ...

最新文章

  1. 搭建rtx服务并客户端登录
  2. 使用elk+redis搭建nginx日志分析平台(引)
  3. python中bs4库_python系统学习2——beautiful soup库(bs4库)学习
  4. pptx模块的图片框
  5. python 爬取全量百度POI
  6. 在android下使用i2c tools
  7. Linux x86-64 IOMMU详解(三)——Intel IOMMU(硬件IOMMU)的功能与基本原理
  8. wps linux2019特色功能,WPS Office 2019 For Linux更新至11.1.0.9522版,附新功能介绍
  9. Excel定义函数自动填充
  10. Docker 三剑客之 Compose
  11. 由人类进化想到软件的作用
  12. 韩顺平零基础循序渐进学Java——自学笔记
  13. iOS 苹果开发者注册 和 App Store问题咨询
  14. openwrt使用port-mirroring
  15. poi ppt 作者属性 修改_ppt文字属性-如何让PowerPoint里作者等信息不被修改自己辛辛苦苦做的工 爱问知识人...
  16. Python篇之编译py文件为pyc文件的方法总结
  17. 单选框(单选)、复选框(多选)
  18. 脑波设备mindwave介绍
  19. 【51单片机实验】4-单片机定时/计数器的应用(附Proteus电路)
  20. Python-int()函数

热门文章

  1. Eclipse-properties文件乱码问题
  2. 计算机科学的鼻祖,现代计算机科学的鼻祖,编程界的上帝
  3. python能熔断吗_在大型项目上,Python 是个烂语言吗?
  4. python 去除水印_基于python的图片修复程序(实现水印去除)
  5. python 全部缩进一行_Python开发工具:缩进规则的使用
  6. oracle按数据条件进行更新_SQL 基础教程, 创建表,按条件选取数据,数据更新,删除...
  7. salesforce 零基础开发入门学习(十四)salesforce中工厂模式的运用
  8. ubuntu 下安装配置LAMP
  9. Servlet学习的两个案例之网站访问次数的统计
  10. C# - 企业框架下的存储过程输出参数