从 C 转向 C++
条款 1 :尽量使用 const 和 inline 而不使用 #define
#define 有很多不确定的东西, eg:
#define max(a,b) ((a) > (b) ? (a) : (b))
int a = 5, b = 0;
max(++a, b); // a 的值增加了 2 次
max(++a, b+10); // a 的值只增加了 1 次
条款2 :尽量使用<iostream> 而不用<stdio.h>
关于这点,主要是作者认为iostream 的cin,cout 都是识别类型的,不像scanf,printf 一样对不同的类型有不同的格式,然而有时候用scanf,printf 的效率要比cin,cout 的效率要高上不少。当然在重载<< 和>> 操作符号的时候,cin 和cout 表现出了强劲的强壮性。
条款3 :尽量使用new 和delete ,而不用malloc 和free
New 和delete 是类型识别的,而malloc 和free 始终是对void * 原汁原味的内存进行操作。而且new delete 常与类的构造和析构函数绑定到一起。
内存管理
条款4 :尽量使用C++ 风格的注释
C 风格的注释对嵌套的注释会出问题,eg :/*/* */*/ 会出错。
条款5 :对应的new 和delete 要采用相同的形式
New <=> delete
New[]<=> delete[]
有时候形式会比较隐蔽,eg:
typedef string AddressLines[4];
string *pal = new AddressLines;
delete pal; // 错误
delete [] pal;// 正确
这里pal 其实是一个stirng[] ,所以delete 的时候要用delete[] ,这里主要是用了个typedef ,使得类型隐蔽了些。
条款6 :析构函数里对指针成员调用delete
条款7 :预先准备好内存不够的情况
当内存不够时,可以抛出异常之前调用客户指定的一个出错处理函数——一般称为new_handler 函数:
Typedef void (*new_handler)();
New_handler set_new_handler(new_handler p) throw();
条款8 :写operator new 和operator delete 时要遵循常规
Void *operator new(size_t size);
Void operator delete(void *rawMemory,size_t size);
条款9 :避免隐藏标准形式的new
Void *operator new(size_t size,new_handler p=0);
调用的时候,就可以用new (function) X;
条款10 :如果写了operator new 就要同时写operator delete
该条款引入了内存池,值得回顾。
条款11 :为需要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符
该条款主要是为了防止内存的泄露和浅拷贝的发生。
条款12 :尽量使用初始化而不要在构造函数里赋值
像const 和引用变量都只能用初始化;
用初始化可以节省不必要的数据成员调用缺省的构造参数。
条款13 :初始化列表中成员列出的顺序和它们在类中声明的顺序相同
初始化成员的顺序,是根据它们在类中声明的顺序而定的,所以让初始化列表的顺序和在类中声明的顺序一致有较高的可读性。
条款 14 :确定基类有虚构函数。
使一个类成为抽象类,但又没有任何别的纯虚函数,做法是,在抽象类的类里声明一个纯虚析构函数。
Eg:
Class AWOV{
Public:
Virtual ~AWOV()=0;
};
AWOV::~AWOV(){}
把虚构函数声明为内联函数并没有意义。
条款 15 :让 operator= 返回 *this 的引用
Eg:
// 保持赋值的连续性
// 如 i=j=k; (i=j)=k; 都应该是合法的。
// 不是 const string& 也不是 string
String &operator=(const char *str){
Return *this;
}
条款 16: 在 operator= 中对所有数据成员赋值
在赋值操作符的参数类型与 this 的类名一样时,要先检查 this 与参数变量的地址是否一样。
Eg:
template<class T>
NamedPtr<T>& NamedPtr<T>::operator=(const NamedPtr<T>& rhs)
{
if (this == &rhs)
return *this;
else ….
}
本条款主要针对子类在重载 = 操作符时,没有对父类的成员进行赋值。也要对父类的成员进行赋值,具体例子如下:
Derived& Derived::operator=(const Derived& rhs)
{
if (this == &rhs) return *this;
static_cast<Base&>(*this) = rhs; // 对 *this 的 Base 部分
// 调用 operator=
y = rhs.y;
return *this;
}
条款17: 在operator= 中检查给自己赋值的情况
要记得检查当进行赋值是自己给自己赋值的情况,为避免无谓的赋值操作浪费时间,而且也为了保证正确性(因为赋值操作常伴随着删除左值原来指向的内存,当自己赋值给自己发生时,就很可能出错)