Effective C++ study note
From c to c plus plus
1. const inline | #define
a. 符号列表
b. 类成员const static
c. define 的缺陷
d. ifdef控制编译过程的作用
2. iostream | studio.h
a. 辩证看待两者:
isotream 缺陷:效率,标准化移植性,构造函数对静态对象初始化顺序
stdio.h缺陷:类型安全,扩展性
b. iostream & iostream.h
3. new delete | malloc free
a. constructor destructor
b. match
4. use c++ comment style
a. c++ style用于行注释以及嵌套注释相当有优势
b. 老的编译器对预处理的时候不认识c++style注释
Memory management
5. match new delete
a. new delete [] 如果不匹配意味着什么,这个是如何实现的?
b. 杜绝数组的typedef
6. call delete in destructor
a. 所有的指针成员都必须释放
7. 准备好内存不足的情况
a. set_new_handler
8. operator new & operator delete 常规
a. 如何重载类的new delete,需要考虑什么样的情况
9. 避免隐藏标准形式的new
a. 如何全面支持标准new地操作,必须保持一致的调用方式。
b. 注意new, delete调用时候是如何传参数的
10. 同时提供new,delete
a. 需要传递这个大小的值吗?如何传递?尤其是继承的时候
Constructor destructor operator=
11. 适时提供拷贝构造以及赋值操作
a. 深拷贝,浅拷贝,逐位拷贝,成员拷贝
b. 类里有指针成员时候提供自己版本拷贝构造,赋值操作,选择(禁用,内存拷贝,引用计数)
12. 初始化列表与构造函数
a. 必须置于初始化列表的情况
b. 两者的含义。(构造函数初始化,以及赋值操作)
c. 效率
13. 初始化列表中成员顺序和它们在类中申明的顺序
a. 类成员是按照它们在类里被声明的顺序初始化的,如果多继承,按照基类生命顺序初始化。与初始化列表无关。
14. 基类有虚析构函数
a. 为什么行为无法预知?
b. 如何使用内联函数包装虚析构函数?
15. 让operator=返回*this地引用
a. 为什么必须返回左值?
b. 临时对象的const属性
16. 在operator=中对所有数据成员赋值
a. 这里的所有成员包括:己类的成员, 所有基类成员。
b. 如何处理基类的成员?
c. 多继承下拷贝构造函数也有这样的问题。
17. 在operator=中检查给自己赋值的情况
a. 检查的重要性:提高效率,保证正确性
b. 如何检查 (值判断,地址判断,对象标识)
c. 扩展,只要别名有可能出现的函数都需要注意这个问题
类与函数
18. 争取类的接口完整并且最小
19. 分清楚成员函数,非成员函数和友元函数
a. 成员函数与非成员函数比较:虽然两者可以对所有参数隐式转换的。但是成员函数的this参数是不放在可转换列表里面的。
b. 友元函数虽然也是属于非成员函数,但是它的不同就是能够访问私有变量。
c. 设计一个函数的时候考虑顺序:成员函数->非成员函数->友元函数:只有非成员函数对最左面的参数进行类型转换,如果f需要对最左面的参数转换,让f成为非成员函数。如果f还需要访问类的非公有成员,让f成为类的友元函数。其他情况下都申明为成员函数。
20. 避免public接口出现数据成员
a. 基于几方面的考虑(一致性,功能分离)
21. 尽可能使用const
a. 星号作为分隔符
b. bitwise constness & conceptual constness
c. mutable or const_cast
22. 尽量用传引用而不是传值
a. slicing-problem while pass value
23. 必须返回一个对象时不要试图返回一个引用
a. for example: operator *
24. 在函数重载和设定参数缺省值间慎重选择
25. 避免对指针和数字类型重载
26. 潜在的二义性
a. 二义性来源:方法二义性,函数重载参数二义性,继承带来的二义性
b. C++语言的标准转换??
c. 对类成员的引用所产生的二义性不考虑访问权限-〉改变一个类成员的访问权限不应该改变程序的含义
27. 如果不想使用隐式生成的函数就要显式地禁止它
a. private申明它但是不去实现,这样链接器会帮你检查错误
28. 划分全局名字空间
类和函数:实现
29. 避免返回内部数据的句柄
a. 指针,引用的返回。注意const版本
30. 避免这样的成员函数:返回值是指向成员非const指针或引用,但成员的访问级比这个函数要低
31. 不要返回局部对象的引用
32. 尽可能的推迟变量定义
a. 不必要的函数调用开销
33. 明智的使用内联
a. 内联的好处
b. 被外联的内联函数的编译器处理上:首先都要承担函数调用的代价,其次旧的编译器还可能作为static函数处理来函数的链接的二义性
c. 慎重选择内联,重视编译器的警告
d. 内联的关键字是针对实现定义部分的,不是针对声明的
e. 取内联函数的地址会导致编译器为此生成一个函数体,认为破坏内联定义
f. 类成员函数声明时候同时定义函数体,默认成内联
g. 在新的编译器上面,内联是一个不稳定的属性,至于在代码里的是否展开不能定论这个函数是否内联
h. 在编译器看来多态始终比内联重要。在编译期间,静态调用是可以展开的,动态的不可以
i. 内联是否展开除了要看本身代码的复杂度外还有其他因素影响:编译器的内联开关,调用方式
j. 在新的编译器看来不管内联的是否展开或是否外联,亦或是否综合使用,记住,代码实体只有一份
k. 虚函数与内联的结合是一个很特殊的例子。
34. 降低文件间的编译依赖性
a. 前向声明 & 句柄类 & 协议类
继承和面向对象设计
C++提供了多种很令人困惑的面向对象构造部件,包括公有、保护和私有基类;虚拟和非虚拟基类;虚拟和非虚拟成员函数。这些部件不仅互相之间有联系,还和C++的其它部分相互作用。所以,对于每种部件的含义、什么时候该用它们、怎样最好地和C++中非面向对象部分相结合?
经典问题:
a. 假如需要设计一组具有共同特征的类,是该使用继承使得所有的类都派生于一个共同的基类呢,还是使用模板使得它们都从一个共同的代码框架中产生?
b. 类A 的实现要用到类B,是让A 拥有一个类型为B 的数据成员呢,还是让A 私有继承于B?
c. 假设想设计一个标准库中没有提供的、类型安全的同族容器类(条款49列出了标准库实际提供的容器类),是使用模板呢,还是最好为某个 "自身用普通(void*)指针来实现" 的类建立类型安全的接口?
35. 公有继承体现‘是一个’的含义
36. 区分接口继承和实现继承
a. 纯虚函数的目的在于:使派生类仅仅是继承函数的接口
b. 简单虚函数的目的在于:使派生类继承函数的接口和缺省实现
c. 非虚函数的目的在于:使派生类继承函数的接口和强制实现
37. 决不要重新定义继承而来得非虚函数
a. 非虚函数是静态绑定,虚函数是动态绑定
38. 决不要重新定义继承而来的缺省参数值
a. 缺省参数值也是静态绑定的
39. 避免‘向下转换’继承层次
a. 向下意味着向派生类转换
b. 如何消除向下转换:虚函数;加强类型约束;dynamic_cast
40. 通过分层来体现‘有一个’或‘用……来实现’
41. 区分继承和模板
a. 两者的区别在于类型T是否影响类的行为
42. 明智的使用私有继承
a. 私有继承常用语实现而非设计
b. 实例化模板导致代码膨胀
c. 示例代码设计:
1 template<class T>
2 class Stack: private GenericStack {
3 public:
4 void push(T *objectPtr) { GenericStack::push(objectPtr); }
5 T * pop() { return static_cast<T*>(GenericStack::pop()); }
6 bool empty() const { return GenericStack::empty(); }
7 };
8
9 class GenericStack {
10 protected:
11 GenericStack();
12 ~GenericStack();
13 void push(void *object);
14 void * pop();
15 bool empty() const;
16 private:
17 // 同上
18 };
19
43. 明智的使用多继承
a. adapter 是一个很好的多继承的例子
b. 虚继承的实现以及构造特性
44. 说你想说的,理解你说想说的
a. 整盘的设计考虑
b. 共同的基类意味着共同的特性; 公有继承意味着是一个, 私有继承意味着用什么来实现, 分层意味着有一个或者用……来实现
c. 对于公有继承而言:纯虚函数意味着继承接口,简单虚函数意味着继承函数的接口加上一个缺省实现,非虚函数意味着继承数的接口加上一个强制实现(特殊性上的不变性)
杂项
45. 弄清楚C++在幕后为你所作的
a. 缺省类成员函数:构造,拷贝构造,析构(虚待定),赋值,取址(2个)
b. 注意:如果需要这些函数才会被生成
c. 赋值操作必须验证合法才能通过编译器
46. 宁可编译和链接时出错,也不要运行时出错
47. 确保非局部静态对象在使用前被初始化
a. 初始化的顺序不确定导致很难捕捉这个静态对象初始化的顺序
b. 解决办法:singleton
48. 重视编译器警告
49. 重视标准库
posted on 2007-04-10 12:23
MicroYang 阅读(334)
评论(0) 编辑 收藏 引用