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标识嵌套丛属类型名称,但不能在基类列或成员初值列中以它作为基类修饰符。