Effective C++ 笔记 (三)
目录

六、 Inheritance and Object-Oriented Design

32. Make sure public inheritance models "is-a"

“public继承”意味is-a。 适用于base classes身上的每一件事情也适用于derived classes身上,因为每一个derived class对象也都是一个base class对象。

33. Avoid hiding inherited names

一个符号名查找的路径:

  1. 查找当前作用域{...}
  2. 查找当前function作用域
  3. 查找当前function所在的class作用域
  4. 查找base class的作用域
  5. 查找class所在namespace的作用域
  6. 查找global作用域

派生类public继承基类后,如果重新定义某一函数名,会导致基类该函数名(包括重载函数)都会被屏蔽掉。 这会破坏is-a的关系。

可以在派生类中使用using声明式using Base::func_name;或转交函数(forwarding functions)解决。

34. Differentiate between inheritance of interface and inheritance of implementation

类设计中成员函数分成两部分:函数接口(函数声明)和函数实现(函数定义)。

  • pure virtual: derived classes只继承函数接口。(这时基类可以定义函数实现,在派生类中手动调用,做为缺省实现)
  • simple(impure) virtual: derived classes继承函数接口和缺省函数实现
  • non-virtual: dervied classes继承函数接口和强制继承函数实现

基类(A)的pure virtual(vfunc=0)可以写函数实现(A:vfunc(...){...}),但是派生类调用时必须用A::vfunc(...);形式。

35. Consider alternatives to virtual functions

  1. 使用NVI(non-virtual interface)手法实现Template Method设计模式。把真正实现操作的函数声明为private virtual,然后定义public non-virtual函数去调用private virtual函数。派生类可以重写private virtual函数。

  2. 利用函数指针或boost::function,std::tr1::function和Strategy设计模式。

  3. 利用在函数类中定义virtual函数和Strategy设计模式。

36. Never redefine an inherited non-virtual function

任何情况下都不应该重新定义一个继承而来的non-virtual函数。

37. Never redefine a function's inherited default parameter value

绝对不要重新定义(根据36规则只能重新定义virtual)一个继承而来的缺省参数值。 因为静态绑定下函数并不从其Base继承缺省参数值,而动态绑定下函数会从其base继承缺省参数值。所以说静态参数值是静态绑定的。

38. Model "has-a" or "is-implemented-in-terms-of" through composition

  • is-a:B:public A,表示B是A的一种,B继承了A的特点,是其特化。
  • has-a:A has-a B,表示B是A的一个属性/成员/字段,只是A的一个组成部分。

39. Use private inheritance judiciously

类的三种继承(对于所有继承,private成员都不能被继承):

  • 使用private继承,父类的protected和public成员在子类中变为private;
  • 使用protected继承,父类的protected和public成员在子类中变为protected;
  • 使用public继承,父类中的protected和public成员不发生改变;

对于private继承,编译器不会自动将一个派生类对象转换为基类对象(不管是copy-by-value或者copy-by-reference时)。 这也意味着private继承不是is-a的关系,其在软件设计层面也没有具体含义。 private继承意味着基类的public和protected成员实现部分被继承,接口部分被略去。

因此可以把private继承转变成has-a的复合关系,使基类成为派生类的一个成员变量。 但碰到基类对象有virtual或protected时,可能用private继承更好一些。

private单一继承可以造成empty base最优化(EBO),从而导致“对象尺寸最小化”。

40. Use multiple inheritance judiciously

multiple inheritance多重继承会引出一个头疼问题:钻石型多重继承:

/-- B <--|
A <-|         |-- D
\-- C <--|

这样会引出一个问题:对于A的成员变量,D拥有一份(B,C共享)还是两份(各自来自B、C)呢? C++的默认做法是拥有两份。

为了达到共享A的目的,C++引入了virtual继承,即B和C是通过virtual继承来继承A的。 但是编译器必须为此提供成本:B, C对象往往比non-virtual继承体积更大, 访问虚基类A的成员变量也更慢。

因此的忠告是:1. 非必要不使用virtual继承; 2. 避免在虚基类中放数据(如Java的Interface)。

七、 Templates and Generic Programming

41. Understand implicit interfaces and compile-time polymorphism

  • 面向对象编程(Object Oriented C++):显示接口(explicit interfaces)(函数签名:函数名称、函数类型、返回类型)和运行期多态(runtime polymorphism)(virtual函数)
  • 泛型编程(Template C++):隐式接口(implicit inferfaces)(有效表达式:template对象符合的条件,如函数、操作符)和编译期多态(compile-time polymorphism)(template函数)

42. Understand the two meanings of typename

template声明式中使用的classtypename可以互换,无区别:

template<class T> class Widget;
template<typename T> class Widget;

但是任何时候当你想要在template中指涉一个嵌套从属类型名称,就必须在紧临它的前一个位置上放上关键字typename。 这种情况的例外就是不能在base class list或member init list内作为base class的修饰符(P206)。

43. Know how to access names in templatized base classes

关于处理模板类继承中使用基类函数的条款,涉及到模板特化问题。

44. Factor parameter-independent code out of templates

讨论Templates多个具现化造成的代码膨胀。

45. Use member function templates to accept "all compatible types"

讨论成员函数模板(member function template)的作用。

46. Define non-member functions inside templates when type conversion are desired

47. Use traits classes for information about types

通过trait classes获取类型信息。

48. Be aware of template metaprogramming

元编程。。。

(@_@ template相关的东西还要学习。。)

八、 Customizing new and delete

  • operator new
  • operator delete
  • operator new []
  • operator delete []

49. Understand the behavior of the new-handler

operator new抛出异常时(通常是内存申请无法满足),首先会调用用户指定的错误处理函数,即new-handler。 这个函数通过set_new_handler来设置。

namespace std {
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
}

50. Understand when it makes sense to replace new and delete

51. Adhere to convention when writing new and delete

52. Write placement delete if you write placement new

  • placement new: 比标准operator new多了若干行参的operator new
  • placement delete: 与placement new对应

九、 Miscellany

53. Play attention to compiler warnings

54. Familiarize yourself with the standard library, include TR1

55. Familiarize yourself with Boost

发表评论