11. 为需要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符
1) 原因:如果不写copy constuctor和operator =, 如果类中含有指针准备指向动态内存,那么在copy或者=的时候会造成2个指针指向同一个对象(bitwise bopy)
2) 当实现拷贝构造函数和赋值操作符非常麻烦的时候,特别是可以确信程序中不会做拷贝和赋值操作的时候,可以只声明这些函数(声明为private成员)而不去定义(实现)它们.
12. 尽量使用初始化而不要在构造函数里赋值
1) const成员只能被初始化,不能被赋值.
2) 对有基类的对象来说,基类的成员初始化和构造函数体的执行发生在派生类的成员初始化和构造函数体的执行之前
3)
对象的创建分两步:
1>. 数据成员初始化。(参见条款13)
2>. 执行被调用构造函数体内的动作。
所以,在进入构造函数体的时候所有对象都已经被构造出来了。
4) 效率问题
类中包含另外一个类对象,如果用初始化列表只调用copy constructor,在函数体内赋值会调用 缺省constructor + operator=.
5) 无默认构造函数问题
如果基类无默认构造函数,派生类必须显示调用基类有的构造函数,否则编译出错。
如果类包含另外一个类的对象,但是被包含的对象所属的类无默认构造函数,你的类必须在初始化列表中显示调用被包含类的构造函数,否则编译出错。
13. 初始化列表中成员列出的顺序和它们在类中声明的顺序相同
14. 确定基类有虚析构函数
如果没有,那么delete pBase;(Base* pBase = new Derived();)时,派生类的析构函数不会得到调用
15. 让operator=返回*this的引用
1) 返回void会导致不能连续赋值
2) 返回右边的对象(参数)会导致编译出错,一般operator=的参数都是const 类型,而返回值是非const的引用。
3) 假设operator=的参数为非const,那么会经常导致自己写的程序出错
x = "hello";
const string temp("hello"); // 产生临时string
x = temp; // 临时string传给operator=
16. 在operator=中对所有数据成员赋值
1) 只要想对赋值过程的某一个部分进行控制,就必须负责做赋值过程中所有的事。
2) 初写这个类时当然很容易记住上面的原则,但同样重要的是,当类里增加新的数据成员时,也要记住更新赋值运算符函数。
3) 当derived的拷贝创建时,没有拷贝其基类部分。当然,这个derived对象的base部分还是创建了,但它是用base的缺省构造函数创建的,成员x被初始化为0. derived的拷贝构造函数必须保证调用的是base的拷贝构造函数而不是base的缺省构造函数
17. 在operator=中检查给自己赋值的情况
c& c::operator=(const c& rhs)
{
// 检查对自己赋值的情况
if (this == &rhs) return *this;
...
}
否则结下来delete this指向的内存会导致灾难。