多重继承是一种比较复杂的继承关系,它意味着如果用户想要使用这个类,那么就要对它的父类也了如指掌,所以在项目中会带来可读性的问题,一般我们都会尽量选择用单继承去替代它。

使用多重继承过程容易碰到的问题就是名字冲突,像下面这样:

 class Base1
{
public:
void fun(){}
}; class Base2
{
private:
void fun(){}
}; class Derived : public Base1, public Base2
{}; int main()
{
Derived d;
d.fun(); // error C2385: 对“fun”的访问不明确
return ;
}

因为在两个父类中都有名为fun的函数,所以这时候编译器不知道用户想调用的是哪个函数。但这里细心的读者会发现,这里我们是把Base2的fun的访问权限设为了private的。这个例子同时也说明了,编译器会优先去查找最合适的重载函数,再去考虑它的可访问性。如果真的要去访问重名的函数,可以指定作用域,像这样d.Base1::fun()(但注意d.Base2::fun()不行,因为它的访问性是private的)。

多重继承另一个容易碰到的问题就是虚继承,我记得这还是面试官的一道面试题。试想一下,有一个父类名为A,类B和类C都继承于A,类D又同时继承了B和C(多重继承),那么如果不做任何处理,C++的类继承图里会包含两份A。

但如果在继承的时候加了virtual,像下面这样:

class B: virtual public A{…}
class C: virtual pulibc A{…}

那么D中就只有一份A了。C++标准库里面的流就是采用这样的形式,有一个父流basic_ios,basic_istream和basic_ostream分别虚继承于basic_ios,而basic_iostream又多重继承于basic_istream和basic_ostream。

为了保证不会出现两份父类,只要是public继承理论上都应该有virutal关键字,但virutal也是有代价的,访问virtual base class的成员变量要比访问non-virutal base class的成员变量速度要慢。所以作者的忠告是:

1. 非必要不使用virtual classes继承,普通情况请使用non-virtual classes继承

2. 如果必须使用virtual base classes,尽可能避免在其中放置数据。

后面的篇幅书上就举了一个多重继承的例子,在这里我就不说了,有兴趣的读者可以自己看看,但个人觉得还是能不用多重继承的时候,就尽量不用它,用复合+单继承往往能达到目的。

最后总结一下:

1. 多重继承比单一继承更复杂。它可能导致新的歧义性,以及对virtual继承的需要。

2. virtual继承会增加大小、速度、初始化(及赋值)复杂度等等成本。如果virtual base classes不带任何数据,将是最具实用价值的情况。

3. 多重继承的确有正当用途。其中一个情节涉及”public继承某个Interface class”和”private继承某个协助实现的class”的两两组合。

读书笔记_Effective_C++_条款四十:明智而审慎地使用多重继承的更多相关文章

  1. 读书笔记_Effective_C++_条款四十九:了解new_handler的行为

    本章开始讨论内存分配的一些用法,C/C++内存分配采用new和delete.在new申请内存时,可能会遇到的一种情况就是,内存不够了,这时候会抛出out of memory的异常.有的时候,我们希望能 ...

  2. 读书笔记_Effective_C++_条款四十八:了解模板元编程

    作为模板部分的结束节,本条款谈到了模板元编程,元编程本质上就是将运行期的代价转移到编译期,它利用template编译生成C++源码,举下面阶乘例子: template <int N> st ...

  3. 读书笔记_Effective_C++_条款四十六:需要类型转换时请为模板定义非成员函数

    这个条款可以看成是条款24的续集,我们先简单回顾一下条款24,它说了为什么类似于operator *这样的重载运算符要定义成非成员函数(是为了保证混合乘法2*SomeRational或者SomeRat ...

  4. 读书笔记_Effective_C++_条款四十五:运用成员函数模板接受所有兼容类型

    比如有一个Base类和一个Derived类,像下面这样: class BaseClass {…}; class DerivedClass : public BaseClass {…}; 因为是父类与子 ...

  5. 读书笔记_Effective_C++_条款四十四:将与参数无关的代码抽离template

    标题上说“将与参数无关的代码抽离template”,这里的参数既可以指类型,也可以是非类型,我们先来看看非类型的情况. 假定我们要为矩阵写一个类,这个矩阵的行列元素个数相等,是一个方阵,因而我们可以对 ...

  6. 读书笔记_Effective_C++_条款四十二:了解typename的双重意义

    顾名思义,typename有双重含意.只要你用过template,那么第一重含意一定知道,那就是声明模板的时候,我们既可以这样写: template <class T> 也可以这样写 te ...

  7. 读书笔记_Effective_C++_条款三十:了解inline的里里外外

    学过基本程序课的同学都知道,inline是内联的关键字,它可以建议编译器将函数的每一个调用都用函数本体替换.这是一种以空间换时间的做法.把每一次调用都用本体替换,无疑会使代码膨胀,但可以节省函数调用的 ...

  8. 读书笔记_Effective_C++_条款三十九:明智而审慎地使用private继承

    private继承的意义在于“be implemented in turns of”,这个与上一条款中说的复合模型的第二层含义是相同的,这也意味着通常我们可以在这两种设计方法之间转换,但书上还是更提倡 ...

  9. 读书笔记_Effective_C++_条款三十四:区分接口继承和实现继承

    这个条款书上内容说的篇幅比较多,但其实思想并不复杂.只要能理解三句话即可,第一句话是:纯虚函数只继承接口:第二句话是:虚函数既继承接口,也提供了一份默认实现:第三句话是:普通函数既继承接口,也强制继承 ...

随机推荐

  1. MySQL5.7之多源复制&Nginx中间件(上)【转】

    有生之年系列----MySQL5.7之多源复制&Nginx中间件(上)-wangwenan6-ITPUB博客http://blog.itpub.net/29510932/viewspace-1 ...

  2. BZOJ4840 NEERC2016 Binary Code

    Problem BZOJ Solution 可能是因为快要省选了,所以最近更博的频率好像高了点_(:зゝ∠)_ 每个字符串最多有两个状态,然后要满足一些依赖关系,考虑2sat.可以先把字符串的结束节点 ...

  3. linux文件管理 -> 系统压缩打包

    如果希望windows和Linux互相能使用的压缩工具, 建议.zip格式 压缩的好处主要有: 节省磁盘空间占用率 节省网络传输带宽消耗 网络传输更加快捷 Linux系统常见的后缀名所对应的压缩工具 ...

  4. node练习笔记

    一.用http模块实现客户端 1.   这个错误的原因是:客户端http_client.js里面的端口和服务端里面的端口不一样 2.querystring.stringify  字符串转换成对象  q ...

  5. [USACO16OPEN]262144

    传送门啦 其实大家可以先看一下这个题 [USACO16OPEN]248 分析: 数据范围很奇特:n特别,a[i]特别——如果O(N^3)能接受就直接区间DP水过了,但是不行,于是考虑设计一个状态囊括a ...

  6. matlab转python

    最近在做把matlab代码转成python代码,没有用过matlab,python也只是局限于爬虫,所以.... matlab与python最大的不同是,matlab的下标是从1开始的,python和 ...

  7. 20165203 2017-2018-2 《Java程序设计》第一周学习总结

    20165203 2017-2018-2<Java程序设计>第一周学习总结 教材学习内容总结 (一)Java的地位 Java是面向对象编程,并涉及网络.多线程等重要的基础知识,是一门很好的 ...

  8. Spring介绍及配置(XML文件配置和注解配置)

    本节内容: Spring介绍 Spring搭建 Spring概念 Spring配置讲解 使用注解配置Spring 一.Spring介绍 1. 什么是Spring Spring是一个开源框架,Sprin ...

  9. mongo oplog 整理

    首先需要介绍一下mongodb local库的作用 local库是MongoDB的系统库,记录着时间戳和索引和复制集等信息 test:PRIMARY> use local switched to ...

  10. sdoi2014-向量集-线段树-二分斜率

    注意到线段树一个节点只有满了才会被用到,那时再建ConvexHull就行了... #include <bits/stdc++.h> using namespace std; namespa ...