《Effective C++》整理
条款2:尽量以const,enmu,inline替换#define
1.使用#define的缺点1:#define的原理是替换,也就是属于编辑范畴。程序开发者无法直接看到编辑后的代码,因此当#define替换引起编译错误时可能会给开发者带来困惑,如果#define定义的位置在其它人写的文件中,那就更让人摸不着头脑了。
2.使用#define的缺点2:#define不提供封装性,#define不重视作用域(除非在某处被#undef)。
3.使用#define的缺点3:可能出现不安全或不可预料的结果。例如在#define中引入“++a”这样的表达式而被该#define在同一条语句中替换产生而出现多处“++a”时。对于这类形似函数的宏,应该使用inline函数替换#define。
条款3:尽可能使用const
1.指针情形const修饰语义辨析:如果关键词const出现在星号左边,表示修饰被指物是常量,如果const出现在星号右边,表示指针自身是常量。
|
|
2.const好处:允许程序员告诉编译器和其他程序员某值应该保持不变,只要“某值保持不变”这件事是事实,就应该说出来,因为说出来可以获得编译器的襄助。
3.对于使用mutable修饰的变量,即使用const修饰的函数也能修改其值。
条款4:确定对象被使用前已被初始化
1.“初始化”与“赋值”容易混淆。
|
|
注意!该构造函数内的的theString、theList都不是被初始化,而是被赋值。初始化发生的时间更早,发生于这些成员的default构造函数被自动调用之时。
其实,该构造函数对theString、theList进行了两步操作:1.分别使用string和list
更好的方式是使用初始化列表替换赋值动作(单次调用copy构造函数更高效):
|
|
2.在同一个编译单元中,C++有十分固定的成员初始化次序:先基类后派生类,每个类的成员变量总是以声明次序被初始化(即使初始化列表中颠倒了次序)。(注:一个编译单元等于一份产出单一目标文件的源码)
3.由于C++对“定义于不同便一单元内的non-local static对象”的初始化次序没有明确定义。可能引发的问题是:某个编译单元内的某个non-local static对象的初始化动作使用了另一编译单元内的某个non-local static对象,被用到的这个对象可能尚未被初始化。
用local static对象替换而避免使用non-local static对象可以解决这一问题。
(注:关于non-local static对象和local static对象的概念见C++中local static对象与non-local static对象的概念与区别)
条款5:了解C++默认编写并调用哪些函数
1.一个empty class经过C++处理过后,编译器就会为它声明一个copy构造函数、一个copy assignment操作符、一个析构函数,且所有这些函数都是public和inline的。也即如果写下代码class Empty{};就好像写下代码:
|
|
而唯有这些函数被需要(被调用)时,它们才会被编译器创建出来。
2.如果在类中声明了一个构造函数,编译器就不再为它创建default构造函数。
3.编译器自动创建的copy构造函数对内置类型的处理是“拷贝每一个bit”(包括指针)。
4.如果想要使一个内含reference成员、const成员的class支持赋值操作,必须自己定义copy assignment操作符,编译器不会自动提供。(因为reference对象、const对象不支持一般的赋值操作,即使是在初始化时也必须使用初始化列表)
5.如果base classes将copyassignment操作符声明为private,编译器将拒绝为其derived classes生成一个copy assignment操作符。
条款6:若不想使用编译器自动生成的函数,就该明确拒绝
如条款5所述,编译器会自动声明一个copy构造函数、一个copy assignment操作符、一个析构函数。但为了不希望某些人尝试调用它们,可以通过显式地将它们声明为private,例如将copy构造函数或copy assignment操作符声明为private。
|
|
条款7:为多态基类声明virtual析构函数
1.C++明白指出,当derived class对象由一个base class指针被删除,而如果该base class带着一个non-virtual析构函数,其结果未定义,实际执行时通常是derived成分没被销毁(只调用base类的析构函数),导致内存泄漏、破坏数据结构。而如果将base class中的析构函数定义为virtual的,此后删除derived class对象就会调用derived class的析构函数。
2.但如果class并不意图被用作一个base class,将它的析构函数设定为virtual的话,也是一个馊主意。也即如果设计中class不含virtual函数,通常表示它不企图被当做base class。(许多人的心得是:class中至少有一个virtual函数,就也将析构函数声明为virtual)