本文只是粗浅讨论一下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. Android Studio突然不显示logcat日志

    参考文章:http://blog.csdn.net/victor_e_n_01185/article/details/52818809 有时候,AS出现没有log的情况.一般您换了模拟器,或者使用真机 ...

  2. Java异常处理-----程序中的异常处理.启蒙

    1.当除数是非0,除法运算完毕,程序继续执行. 2.当除数是0,程序发生异常,并且除法运算之后的代码停止运行.因为程序发生异常需要进行处理. class Demo { public static vo ...

  3. JBOSS EAP 6 系列六 公共模块的jar配置到jboss的modules详细配置

    公司项目中遇到并要解决的问题 1:原则上除了自己写的代码之外,公共的jar不应该都在打包的时候打包到ear里面,这样的话包太大,也不符合的分层的逻辑,在jboss容器内部,每个ear的包重复jar都会 ...

  4. [error]error while loading shared libraries: libpcre.so.1 解决

    nginx 安装好之后,启动的时候报错 [root@localhost nginx-1.6.2]# /usr/local/nginx/sbin/nginx  /usr/local/nginx/sbin ...

  5. 最近邻查找算法kd-tree

    http://blog.csdn.net/pipisorry/article/details/52186307 )选择特征(坐标轴)的方法  (2)以该特征的哪一个为界 (3)达到什么条件算法结束. ...

  6. 插件占坑,四大组件动态注册前奏(三) 系统BroadCast的注册发送流程

    转载请注明出处:http://blog.csdn.net/hejjunlin/article/details/52204143 前言:为什么要了解系统Activity,Service,BroadCas ...

  7. spring 的OpenSessionInViewFilter简介

    假设在你的应用中Hibernate是通过spring 来管理它的session.如果在你的应用中没有使用OpenSessionInViewFilter或者OpenSessionInViewInterc ...

  8. Dynamics CRM2011 在Visual Studio中开启Javascript的Xrm.Page智能提示

    前面一篇博文:http://blog.csdn.net/vic0228/article/details/49512699 讲到了在Visual Studio中开启xml编辑的智能提示,本篇接着来讲下如 ...

  9. UNIX网络编程——使用select 实现套接字I/O超时

    下面程序包含read_timeout.write_timeout.accept_timeout.connect_timeout 四个函数封装: /* read_timeout - 读超时检测函数,不含 ...

  10. Qualcomm平台camera调试移植入门

    1  camera基本代码架构 高通平台对于camera的代码组织,大体上还是遵循Android的框架:即上层应用和HAL层交互,高通平台在HAL层里面实现自己的一套管理策略:在kernel中实现se ...