C++虚继承时的构造函数
在虚继承中,虚基类是由最终的派生类初始化的,换句话说,最终派生类的构造函数必须要调用虚基类的构造函数。对最终的派生类来说,虚基类是间接基类,而不是直接基类。这跟普通继承不同,在普通继承中,派生类构造函数中只能调用直接基类的构造函数,不能调用间接基类的。
下面我们以菱形继承为例来演示构造函数的调用:
#include <iostream>
using namespace std;class A{public:A(int a);protected:int m_a;
};A::A(int a):m_a(a) {}class B:virtual public A{public:B(int a,int b);public:void display();protected:int m_b;};B::B(int a, int b):A(a),m_b (b) {}void B::display() {cout<<"m_a="<<m_a<<", m_b="<<m_b<<endl;
}class C:virtual public A{public:C(int a,int c);public:void display();protected:int m_c;};C::C(int a,int c):A(a),m_c(c){}void C::display() {cout<<"m_a="<<m_a<<" ,m_c="<<m_c<<endl;
}//间接派生类D;class D:public B,public C{public:D(int a,int b,int c,int d);public:void display();private:int m_d;};D::D(int a, int b, int c, int d) :A(a),B(a,b),C(a,c),m_d(d){}void D::display(){cout<<"m_a="<<m_a<<", m_b="<<m_b<<", m_c="<<m_c<<", m_d="<<m_d<<endl;
}int main(){B b(10,20);b.display();C c(30,40);c.display();D d(50,60,70,80);d.display();return 0;}
运行结果:
m_a=10, m_b=20
m_a=30, m_c=40
m_a=50, m_b=60, m_c=70, m_d=80
请注意第 50 行代码,在最终派生类 D 的构造函数中,除了调用 B 和 C 的构造函数,还调用了 A 的构造函数,这说明 D 不但要负责初始化直接基类 B 和 C,还要负责初始化间接基类 A。而在以往的普通继承中,派生类的构造函数只负责初始化它的直接基类,再由直接基类的构造函数初始化间接基类,用户尝试调用间接基类的构造函数将导致错误。
现在采用了虚继承,虚基类 A 在最终派生类 D 中只保留了一份成员变量 m_a,如果由 B 和 C 初始化 m_a,那么 B 和 C 在调用 A 的构造函数时很有可能给出不同的实参,这个时候编译器就会犯迷糊,不知道使用哪个实参初始化 m_a。
为了避免出现这种矛盾的情况,C++ 干脆规定必须由最终的派生类 D 来初始化虚基类 A,直接派生类 B 和 C 对 A 的构造函数的调用是无效的。在第 50 行代码中,调用 B 的构造函数时试图将 m_a 初始化为 90,调用 C 的构造函数时试图将 m_a 初始化为 100,但是输出结果有力地证明了这些都是无效的,m_a 最终被初始化为 50,这正是在 D 中直接调用 A 的构造函数的结果。
另外需要关注的是构造函数的执行顺序。虚继承时构造函数的执行顺序与普通继承时不同:在最终派生类的构造函数调用列表中,不管各个构造函数出现的顺序如何,编译器总是先调用虚基类的构造函数,再按照出现的顺序调用其他的构造函数;而对于普通继承,就是按照构造函数出现的顺序依次调用的。
修改本例中第 50 行代码,改变构造函数出现的顺序:
D::D(int a, int b, int c, int d): B(90, b), C(100, c), A(a), m_d(d){ }
虽然我们将 A() 放在了最后,但是编译器仍然会先调用 A(),然后再调用 B()、C(),因为 A() 是虚基类的构造函数,比其他构造函数优先级高。如果没有使用虚继承的话,那么编译器将按照出现的顺序依次调用 B()、C()、A()。
C++虚继承时的构造函数相关推荐
- Cpp 对象模型探索 / 带有虚继承类的构造函数的调用顺序
栗子 #include <iostream> class A { public:A() { std::cout << "A" << std::e ...
- C++中的虚继承与虚基类
1.Cpp中的虚继承与虚基类 在多继承时,很容易产生命名冲突的问题,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如典型的是菱形继承,如下图所示: 类A派 ...
- C++学习笔记day47-----C++98-继承中的构造函数,析构函数,拷贝构造函数,拷贝赋值函数,多重继承,虚继承
继承中的构造函数 当通过一个子类创建一个新的对象时,编译器会根据子类在初始化表中指明的基类的初始化方式去调用基类相应的构造函数.如果子类的初始化表中,并没有指明基类的初始化方式,编译器将会调用基类的无 ...
- Th3.15:继承的构造函数、多重继承、虚继承之详述
本博客将记录:新经典课程知识点的第15节的笔记! 本小节的知识点分别是继承的构造函数.多重继承.虚继承. 今天总结的知识分为以下5个点: (1)继承的构造函数 (2)多重继承 (2.1)多重 ...
- c++基础知识点(6)类的继承,构造,析构顺序,虚继承等
学习继承之前,我们先回顾下protected(保护)成员 protected 成员的特点与作用: -对建立其所在类对象的模块来说,它与private成员的性质相同 -对于其派生类来说,它与public ...
- C++:94---类继承(菱形继承、虚继承(virtual虚基类))
一.菱形继承 在介绍虚继承之前介绍一下菱形继承 概念:A作为基类,B和C都继承与A.最后一个类D又继承于B和C,这样形式的继承称为菱形继承 菱形继承的缺点: 数据冗余:在D中会保存两份A的内容 访问不 ...
- C++:钻石继承与虚继承
QUESTION:什么是钻石继承? ANSWER:假设我们已经有了两个类Father1和Father2,他们都是类GrandFather的子类.现在又有一个新类Son,这个新类通过多继承机制对类Fat ...
- C++虚继承下的内存模型(二)
对于虚继承,恰恰和普通继承相反,大部分编译器会把基类成员变量放在派生类成员变量的后面,这样随着继承层级的增加,基类成员变量的偏移就会改变,就得通过其他方案来计算偏移量. 下面我们来一步一步地分析虚继承 ...
- C++虚继承下的内存模型(一)
简单的面向对象,只有单继承或多继承的情况下,内存模型很好理解,编译器实现起来也容易,C++ 的效率和 C 的效率不相上下.一旦和 virtual 关键字扯上关系,使用到虚继承或虚函数,内存模型就变得混 ...
最新文章
- 函数指针--全局函数指针与类的函数指针(二)
- kicad绿油开窗_GitHub - OS-Q/S05: KiCad EDA
- 计算机应用基础 黄国兴 研读,《计算机应用基础》大纲解读黄国兴
- 利用PIX来搭建×××
- Oracle技术之和分区表相关的一点总结(四)
- 转sql exists和not exists用法
- Angular自学笔记(?)DI提供者
- 十、LINQ查询之延迟执行
- 树结构练习——判断给定森林中有多少棵树
- 骑行318、 2016.7.14
- 如何获取foreach循环当前迭代的索引?
- 如何在Linux和Mac中清除Bash历史记录
- 【IOS账号分享】《帕斯卡契约》
- 汇编程序编译连接过程
- AI教程之谷歌的新 Imagen 视频从文本生成视频
- ERP系统-应收应付子系统-付款单
- 基于java的拼图游戏的设计与实现
- 从EDN上看的文章 如何提高手机的待机时长
- Silverlight新型的富媒体
- h软件_Qt编写软件运行时间记录
热门文章
- 【OMP信道估计】基于OMP压缩感知的信道估计算法的MATLAB仿真
- upc组队赛1 小C的数学问题【单调栈】(POJ2796)
- Linux基础命令---more
- Android自定义View之仿QQ侧滑菜单实现
- progress与meter的区别
- solrcloud replica collection core shard slice 概念讲解
- 操作系统的安装与启动基本原理
- 系统集成资质培训 - 标准系列 -软件文档管理指南
- Enabling Redo Log Transport Compression with active dataguard
- wsl 或者window terminal 下出现Java 14 switch expressions unrecognized解决方法:升级java jdk到14 or later