本文只是粗浅讨论一下C++中的多重继承和虚拟继承。

多重继承中的构造函数和析构函数调用次序

  我们先来看一下简单的例子:

 #include <iostream>
using namespace std; class A
{
private:
char idA; public:
A(){
idA = 'A';
cout << "Constructor of A is called!" << endl;
}
~A() { cout << "Destructor of A is called!" << endl; }
}; class B : public A
{
private:
char idB; public:
B(){
idB = 'B';
cout << "Constructor of B is called!" << endl;
}
~B() { cout << "Destructor of B is called!" << endl; }
}; class C : public A
{
private:
char idC; public:
C(){
idC = 'C';
cout << "Constructor of C is called!" << endl;
}
~C() { cout << "Destructor of C is called!" << endl; }
}; class D : public B, public C
{
private:
char idD; public:
D(){
idD = 'D';
cout << "Constructor of D is called!" << endl;
}
~D() { cout << "Destructor of D is called!" << endl; }
}; int main()
{
D d;
return ;
}

  上述程序的输出为:

  

  由上边结果可以看出,析构函数调用次序跟构造函数是相反的。另外,构造函数调用次序跟类D继承B、C次序(public B, public C)相关。

  可能我们也发现了,对于类D的实例d来说,它其实有两个重复的A实例。我们应该要去掉其中一个以节省空间。具体做法就是采用虚拟继承的方法:

 class B : public virtual A
{
...
}; class C : public virtual A
{
...
};

  这是程序的输出就会变成:

  

  可见这个时候类D的实例d就只有一个类A实例。

二义性

  请看下边程序:

 #include <iostream>
using namespace std; class A
{
private:
char idA; public:
A(){
idA = 'A';
cout << "Constructor of A is called!" << endl;
}
~A() { cout << "Destructor of A is called!" << endl; }
char getID() { return idA; }
}; class B : public virtual A
{
private:
char idB; public:
B(){
idB = 'B';
cout << "Constructor of B is called!" << endl;
}
~B() { cout << "Destructor of B is called!" << endl; }
char getID() { return idB; }
}; class C : public virtual A
{
private:
char idC; public:
C(){
idC = 'C';
cout << "Constructor of C is called!" << endl;
}
~C() { cout << "Destructor of C is called!" << endl; }
char getID() { return idC; }
}; class D : public B, public C
{
private:
char idD; public:
D(){
idD = 'D';
cout << "Constructor of D is called!" << endl;
}
~D() { cout << "Destructor of D is called!" << endl; }
// char getID() { return idD; }
}; int main()
{
D d;
cout << d.getID() << endl; return ;
}

  在main函数中,第63行的d.getID()会优先在类D中查找有没有getID()的定义,如果没有就会到其父类查找;而恰好其父类B、C(同级)均定义了相同的getID()(类A的getID()定义存不存在都没关系),这时d.getID()就不知道要调用B类中的getID()还是C类中的,从而导致二义性。

  不过我们可以通过d.B::getID()、d.C::getID()来指明具体要调用哪一个类的getID。但我们总不会想到这样子去做,而且这样子做也比较麻烦。

虚函数

  对于多重继承的虚函数同样存在二义性。

  先看一下程序:

 #include <iostream>
using namespace std; class A
{
private:
char idA; public:
A(){
idA = 'A';
cout << "Constructor of A is called!" << endl;
}
~A() { cout << "Destructor of A is called!" << endl; }
char getID() { return idA; }
}; class B : public virtual A
{
private:
char idB; public:
B(){
idB = 'B';
cout << "Constructor of B is called!" << endl;
}
~B() { cout << "Destructor of B is called!" << endl; }
char getID() { return idB; }
}; class C : public virtual A
{
private:
char idC; public:
C(){
idC = 'C';
cout << "Constructor of C is called!" << endl;
}
~C() { cout << "Destructor of C is called!" << endl; }
char getID() { return idC; }
}; class D : public B, public C
{
private:
char idD; public:
D(){
idD = 'D';
cout << "Constructor of D is called!" << endl;
}
~D() { cout << "Destructor of D is called!" << endl; }
char getID() { return idD; }
}; int main()
{
D d;
A a = d;
B b = d;
C c = d;
cout << a.getID() << endl;
cout << b.getID() << endl;
cout << c.getID() << endl;
cout << d.getID() << endl; return ;
}

  程序输出如下:

  

  上边程序第63~65行相当于a、b、c将d进行了分割(函数是否是虚函数在这里并无关系,而且注意这里的a、b、c、d都不是指针),分割出属于自己的部分,所以调用getID()的时候能正确反映具体的类。

  

  我们再来看一个程序:

 #include <iostream>
using namespace std; class A
{
private:
char idA; public:
A(){
idA = 'A';
cout << "Constructor of A is called!" << endl;
}
~A() { cout << "Destructor of A is called!" << endl; }
virtual char getID() { return idA; }
}; class B : public virtual A
{
private:
char idB; public:
B(){
idB = 'B';
cout << "Constructor of B is called!" << endl;
}
~B() { cout << "Destructor of B is called!" << endl; }
virtual char getID() { return idB; }
}; class C : public virtual A
{
private:
char idC; public:
C(){
idC = 'C';
cout << "Constructor of C is called!" << endl;
}
~C() { cout << "Destructor of C is called!" << endl; }
virtual char getID() { return idC; }
}; class D : public B, public C
{
private:
char idD; public:
D(){
idD = 'D';
cout << "Constructor of D is called!" << endl;
}
~D() { cout << "Destructor of D is called!" << endl; }
virtual char getID() { return idD; }
}; int main()
{
D *d = new D();
A *a = d;
B *b = d;
C *c = d;
cout << a->getID() << endl;
cout << b->getID() << endl;
cout << c->getID() << endl;
cout << d->getID() << endl; delete d;
return ;
}

  程序的输出如下:

  

  从输出结果可以看出,类D的getID覆盖其所有父类的getID。需要注意的是,当我们在类D的一个父类,如A中不设定getID为虚函数,则“A *a = d”的效果仍然跟分割d指向的内存的效果一样。

C++多重继承与虚拟继承的更多相关文章

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

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

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

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

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

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

  4. C++ 虚拟继承

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

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

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

  6. 虚拟继承C++

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

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

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

  8. C++中的多重继承与虚继承的问题

    1.C++支持多重继承,但是一般情况下,建议使用单一继承. 类D继承自B类和C类,而B类和C类都继承自类A,因此出现下图所示情况: A          A \          / B     C ...

  9. c++,为什么要引入虚拟继承

      虚拟基类是为解决多重继承而出现的.   以下面的一个例子为例: #include <iostream.h> #include <memory.h> class CA { i ...

随机推荐

  1. 判断&数学&生活

    作者:黄永刚 初次接触<概率论与数理统计>这门课的时候,脑袋中只有三个词:黑球.白球.袋子,所有的课程内容就是先取,后取,接触一月之后成功的被放趴下了,因此对于这门课程是没有什么好感的,考 ...

  2. Android图表库MPAndroidChart(四)——条形图的绘制过程过程,隐隐约约我看到了套路

    Android图表库MPAndroidChart(四)--条形图的绘制过程过程,隐隐约约我看到了套路 在学习本课程之前我建议先把我之前的博客看完,这样对整体的流程有一个大致的了解 Android图表库 ...

  3. 从1....n中随机输出m个不重复的数

    void knuth(int n, int m) { srand((unsigned) time( NULL)); for (int i = 0; i < n && m; i++ ...

  4. Android开发基础规范(一)

    转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52602487 前言:Androi ...

  5. 【伯乐在线】最值得阅读学习的 10 个 C 语言开源项目代码

    原文出处: 平凡之路的博客   欢迎分享原创到伯乐头条 伯乐在线注:『阅读优秀代码是提高开发人员修为的一种捷径』http://t.cn/S4RGEz .之前@伯乐头条 曾发过一条微博:『C 语言进阶有 ...

  6. 剑指offer面试题5 从头到尾打印链表(c)

  7. linux下查看Memcached运行状态

    查看Memcached运行状态的命令是:echo stats | nc 127.0.0.1 11211 查看memcached状态的基本命令,通过这个命令可以看到如下信息: STAT pid 2245 ...

  8. GDAL书籍中删除数据勘误(C#语言)

    GDAL书籍中关于C#版本删除数据的时候,不能完全删除数据,由于我对C#不了解导致代码有点问题,非常感谢@Bingoyin指出并给出修改方案.此外对于栅格图像的删除.重命名,矢量数据的删除和重命名都有 ...

  9. (NO.00005)iOS实现炸弹人游戏(九):游戏主角(二)

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 上篇介绍了游戏主角的初始化方法,下面我们一次来实现主角的其他方 ...

  10. python在windows下使用setuptools安装egg文件

    最近和同学做个东西,需要安装python的第三方函数库,看了网上的介绍,很是麻烦,这是我实践总结出来的,希望对大家有用. 以安装第三方库networkx 为例,其余函数库都是一个套路,看完就会滴. 1 ...