EC40

40. 明智而审慎地使用多重继承
  • 多重继承可能带来的风险:继承类从一个以上的基类继承相同名称(如函数和typedef等),可能会导致较多的歧义。

  • 当一个函数用virtual修饰了,那就说明要在继承类对其重新定义,不然就没必要用virtual修饰啦

  • C++解析重载函数调用的规则时:在一个继承多个基类的继承类中,如果继承类调用的A函数同时存在多个基类中,C++首先会确认继承类调用的A函数的参数列表和返回值和基类的哪个是最匹配的(但不会看其是属于protected还是private的),那么就调用对应基类的A函数。
    解决歧义的方法:明确指出要调用哪个基类的函数。如继承类对象mp调用基类BaseClass的A函数:mp.BaseClass::A();

  • 什么是会导致要命的“钻石型多重继承”:一个继承体系中,某个基类和某个继承类之间有一条以上的相同路线。
    在这里插入图片描述

    如上图所示,IOFile到File之间就有两个路径。C++默认复制两份基类File的成员变量。如果我们只想要复制一份基类的成员变量,那么就要令带有数据的基类成为virtual base class
    为了在继承类中重新定义virtual函数,那么就要对基类进行virtual继承。

  • virtual继承需要付出成本代价:
    1)virtual继承的类产生的对象比non-virtual继承的对象体积大
    2)访问virtual基类的成员变量比访问non-virtual基类的成员速度慢
    3)virtual基类的初始化责任由继承中的最底层的类负责

  • 如非必要,不用virtual bases和virtual继承。若一定要用virtual base class,那么尽可能避免在其中放置数据,从而避免继承类要对其进行初始化和赋值。
    eg:

    class IPerson
    {
     public:
        virtual ~IPerson();
        virtual string name() const = 0;
        virtual string birthDate() const = 0;
    }
    

总结

多重继承比单一继承复杂,因为会导致新的歧义性,为了避免这种歧义性,我们需要virtual继承。但virtual继承是有成本代价的,除非基类是不包含数据(即成员变量)。

41. 了解隐式接口和编译期多态
  • 泛型编程写出的代码和所处理的对象类型彼此独立,如STL算法中的for_each、find和merge

  • 模板元编程,可以用来创造出“在C++编译期内执行并于编译完成时时停止执行”的程序。【不懂啥意思,先往下走吧~】

  • 面向对象编程世界:总是以显式接口和运行期多态解决问题。
    1)显式接口:指的是它在源码中明确可见
    2)运行期多态:函数用virtual修饰,因此其调用将表现出运行期多态。

  • Template和泛型编程的世界:与面向对象有根本上的不同,显式接口和运行期多态重要性降低。隐式接口和编译期多态移到前头。

  • 运行期多态和编译期多态之间的差异:
    前者发生在运行期,类似于“哪一个virtual函数该被绑定”;
    后者发生在编译期,类似于“哪一个重载函数该被调用”

  • 显式接口与隐式接口之间的差异:
    前者可以理解为public接口,即客户可以访问的东西,包括函数和变量,它是基于函数签名式的。

    后者并不是基于函数签名式,它是由有效表达式组成的。如

    T w;
    if(w.size() > 10 && w != someNastyWigdet)
        ...
    
  • 函数的签名式:由函数名称、参数类型和返回类型构成。

  • template具现化:涉及template对象如w的任何函数调用就有可能造成template具现化。

总结:classes和templates都支持接口和多态,但对类而言接口是显式的,以函数签名为中心,多态是通过virtual函数发生于运行期;对模板参数而言,接口是隐式的,基于有效表达式,多态是通过函数重载发生于编译期。

42. 了解typename的双重意义
  • 声明template类型参数时,最好还是用typename修饰T,eg:

    template<typename T> class Widget;
    
  • std::iterator_traits<IterT>::value_type temp;意思是:类型为iterT的对象所指之物的类型。
    eg:如果IterT是vector<int>,那么temp类型为int;如果IterT是vector<string>, 那么temp类型为string。

  • 嵌套丛属名称:T在类内呈嵌套状,只要不是直接修饰变量都可以认为它是嵌套丛属名称。
    eg:

    T::const_iterator x;
    

    这时候要在其前面加个typename,不然会导致解析困难。

    typename T::const_iterator x;
    
  • 非丛属名称:直接用T修饰变量X
    eg:

    T x;	
    
  • typename必须作为嵌套丛属类型名称的前缀词。

总结:使用typename标识嵌套丛属类型名称,但不能在基类列或成员初值列中以它作为基类修饰符。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值