本文只是粗浅讨论一下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. 根据ccid取得账户,更改某段值再创建账户,返回新的ccid

    CREATE OR REPLACE PACKAGE cux_cuxaprebate_utl IS * =============================================== * ...

  2. Dynamics CRM2013/2015 检索实体属性的两种方式

    昨天有朋友问起如何查询一个字段属性是否存在于某个实体中,一般这个问题我们会采取最直观的查询方式即MetadataBrowser,该工具是一个zip解决方案包在SDK中的如下目录内"\SDK\ ...

  3. GDAL库三个读取Jpeg2000格式驱动测试

    0.目的 GDAL库中提供了四五种读取Jpeg2000的驱动,但是各个驱动读取数据的效率各不相同,下面就针对三种读取jpeg2000的效率进行测试. GDAL库中提供的读取Jpeg2000的驱动有下面 ...

  4. 实现string到double的转换

    分析:此题虽然类似于atoi函数,但毕竟double为64位, 而且支持小数,因而边界条件更加严格,写代码时需要更加注意. #include <errno.h> #include < ...

  5. EBS HRMS数据表

    4.1. 人员基本息                  表            (PER_ALL_PEOPLE_F)                                          ...

  6. 【IOS 开发】基本 UI 控件详解 (UISegmentedControl | UIImageView | UIProgressView | UISlider | UIAlertView )

    转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/50163725 一. 分段控件 (UISegmentedControl) 控件展 ...

  7. J2EE进阶(十三)Spring MVC常用的那些注解

    Spring MVC常用的那些注解 前言 Spring从2.5版本开始在编程中引入注解,用户可以使用@RequestMapping, @RequestParam,@ModelAttribute等等这样 ...

  8. Android布局性能优化—从源码角度看ViewStub延迟加载技术

    在项目中,难免会遇到这种需求,在程序运行时需要动态根据条件来决定显示哪个View或某个布局,最通常的想法就是把需要动态显示的View都先写在布局中,然后把它们的可见性设为View.GONE,最后在代码 ...

  9. (NO.00004)iOS实现打砖块游戏(五):游戏场景类

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 创建游戏场景类头文件 在Xcode创建新GameScene类,继 ...

  10. xml解析之使用dom4j的api对xml文件进行CRUD(二)

    在使用dom4j的api对xml文件进行CRUD(一)见博客http://blog.csdn.net/qq_32059827/article/details/51524330的基础上,再对做一次练习. ...