C++多重继承与虚继承分析

 更新时间:2014年08月14日 17:44:01   投稿:shichen2014  
这篇文章主要介绍了C++多重继承与虚继承分析,是面向对象程序设计非常重要的知识点,需要的朋友可以参考下

本文以实例形式较为全面的讲述了C++的多重继承与虚继承,是大家深入学习C++面向对象程序设计所必须要掌握的知识点,具体内容如下:

一、多重继承

我们知道,在单继承中,派生类的对象中包含了基类部分 和 派生类自定义部分。同样的,在多重继承(multiple inheritance)关系中,派生类的对象包含了每个基类的子对象和自定义成员的子对象。下面是一个多重继承关系图:

class A{ /* */ }; 
class B{ /* */ };  
class C : public A { /* */ }; 
class D : public B, public C { /* */ }; 

C继承了A,派生类D又继承了B和C,如图所示,一个D对象中含有一个B部分、一个C部分(其中又含有一个A部分)以及在D中声明的非静态数据成员:

构造与析构:

构造一个派生类对象将首先构造它的所有基类子对象,其中基类的构造顺序与派生列表中基类的出现顺序保持一致,即B –> A –> C –> D。
销毁一个派生类对象的顺序正好与其创建的顺序相反,即析构函数的调用顺序正好与构造函数相反,即D –> C –> A –> B。注意派生类的析构函数只负责清除派生类本身分配的资源(析构函数体),派生类的成员及基类都是自动销毁的(隐式析构阶段)。

类型转换:

在多重继承的情况下,可以令某个可访问基类的指针或引用直接指向一个派生类对象。编译器不会在派生类向基类的几种转换中进行比较和选择,在它看来转换到任意一种基类都一样好。

二、虚继承

尽管在派生列表中不允许同一个基类出现两次,但实际上派生类可以多次继承同一个类。

派生类通常会含有继承链上每个类对应的子部分。在上面的两种情况中,class D都间接地继承了class A两次,那么意味着class D中包含了class A的两份拷贝。所以在一个class D的对象中将含有2组class A的成员,此时若不加前缀限定符直接使用某个成员将引发“二义性”错误:

class A{ 
public: 
  A():str("name"){}; 
  string str; 
  void print(){cout << str << endl;}; 
}; 
 
class B : public A { }; 
class C : public A { }; 
class D : public B, public C { }; 
 
int main(){ 
  D d; 
  d.str = "songlee"; // 错误:对成员‘str'的请求有歧义 
  d.print(); // 错误:对成员‘print'的请求有歧义 
  return 0; 
} 

当然你可以使用作用域 d.B::str="songlee"; 和 d.B::print(); 来规避“二义性”错误,但这并没有从根本上解决问题。

为了解决上述问题,C++提供了虚继承(virtual inheritance)的机制。虚继承的目的是令某个类作出声明,承诺愿意共享它的基类。其中,共享的基类子对象称为虚基类。在这种机制下,不论虚基类在继承体系中出现多少次,在派生类中都只包含唯一一个共享的虚基类子对象。我们指定虚基类的方式是在派生列表中添加关键字virtual:

class A{ 
public: 
  A():str("name"){}; 
  string str; 
  void print(){cout << str << endl;}; 
}; 
 
class B : virtual public A { };  // 虚继承,A为虚基类 
class C : virtual public A { };  // 关键字public和virtual的顺序随意 
class D : public B, public C { }; 
 
int main(){ 
  D d; 
  d.str = "songlee";  // 正确 
  d.print();      // 正确 
  return 0; 
} 

通过在派生列表中添加virtual(关键字public和virtual的顺序随意)指定A为虚基类,B和C将共享A的同一份实例,这样在D的对象中也将只有A的唯一一份实例,所以A的成员可以被直接访问,并且不会产生二义性。

虚继承最典型的应用是iostream继承于istream和ostream,而istream和ostream虚继承于ios:

class istream : virtual public ios { /* */ }; 
class ostream : virtual public ios { /* */ }; 
class iostream : public istream, public ostream { /* */ }; 

此外还需要注意:

1.支持向基类的常规类型转换。也就是说即使基类是虚基类,也能通过基类的指针或引用操作派生类的对象。

2.虚继承只是解决了一个派生类对象中存在同一个基类的多份拷贝的问题,并没有解决多个基类存在同名成员的二义性问题。

3.在虚继承中,虚基类是由最低层的派生类负责初始化的。如上例中,当创建一个D对象时,D位于派生的最低层并由它负责初始化共享的A基类部分。

4.含有虚基类的对象的构造顺序与一般的多重继承的构造顺序稍有区别:先初始化虚基类子对象(最低层派生类负责),然后按派生列表中的顺序依次对直接基类(非虚)进行初始化。

5.析构的顺序与构造的顺序正好相反。

相关文章

  • C连接Mysql数据库代码

    C连接Mysql数据库代码

    使用C语言连接Mysql数据库的方法,大家可以看看
    2013-11-11
  • C/C++高精度(加减乘除)算法的实现

    C/C++高精度(加减乘除)算法的实现

    高精度的算法,一般的方式是用一个很长的数组去记录数据,数组的每一位记录固定位数的数字,记录顺序是低位到高位。本文将通过实例为大家介绍下高精度算法的实现,感兴趣的可以了解一下
    2022-12-12
  • C语言库函数qsort的使用详解

    C语言库函数qsort的使用详解

    C语言库函数中的qsort的是一个回调函数,回调函数就是一个通过函数指针调用的函数,这篇文章主要介绍了C语言库函数qsort的使用,需要的朋友可以参考下
    2022-06-06
  • C语言中的文件读写fseek 函数

    C语言中的文件读写fseek 函数

    这篇文章主要介绍是我是C语言中的文件读写fseek 函数的相关资料,fseek 函数用来移动文件流的读写位置;就好比播放器,可以直接拖拽到精彩的时间点一样,下面我们就来详细介绍该内容吧,感兴趣的小伙伴可以参考一下
    2021-10-10
  • C语言中判断两数组中是否有相同的元素

    C语言中判断两数组中是否有相同的元素

    下面是我在做IF语句练习时遇到的一个练习题,想要整理在博客上判断两个数组中是否有相同的元素,需要的朋友可以参考下
    2019-10-10
  • cocos2dx实现橡皮擦效果以及判断是否擦除完毕

    cocos2dx实现橡皮擦效果以及判断是否擦除完毕

    这篇文章主要为大家详细介绍了cocos2dx实现橡皮擦效果以及判断是否擦除完毕,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • C语言结构体(struct)常见使用方法(细节问题)

    C语言结构体(struct)常见使用方法(细节问题)

    这篇文章主要介绍了C语言结构体(struct)常见使用方法(细节问题),需要的朋友可以参考下
    2017-03-03
  • Qt实现获取基本网络信息

    Qt实现获取基本网络信息

    这篇文章主要为大家详细介绍了Qt实现获取基本网络信息的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-04-04
  • 详解C++ STL模拟实现list

    详解C++ STL模拟实现list

    这篇文章主要为大家详细介绍了C++如何模拟实现STL容器list,文中的示例代码讲解详细,对我们学习C++有一定帮助,需要的可以参考一下
    2023-01-01
  • C++中继承与组合的区别详细解析

    C++中继承与组合的区别详细解析

    C++的“继承”特性可以提高程序的可复用性。正因为“继承”太有用、太容易用,才要防止乱用“继承”
    2013-09-09

最新评论