请记住:
C++11中有6个:
1、构造
2、析构
3-4、拷贝构造和赋值操作符
5-6、移动构造和赋值操作符
请记住:
C++11中可以直接使用
= delete
来声明拷贝构造函数,显示禁止编译器生成该函数。
virtual
析构函数带有多态性质的基类必须将析构函数声明为虚函数,防止指向子类的基类指针在被释放时只局部销毁了基类对象。
请记住:
C++11可以用
final
定义不想作为基类的类。
析构函数中抛出异常,会让析构动作停止,这可能会导致资源泄漏。
尽量在析构函数中解决所有异常。
请记住:
析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序
如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么 class 应该提供一个普通函数(而非在析构函数中)执行该操作
virtual
函数有继承的构造顺序:先构造基类,再构造子类(和打好地基再建房子一个道理)
有继承的析构顺序:先析构子类,再析构基类(一点点拆)
在基类构造期间,虚函数绝对不会下降到子类层(即:在基类构造期间,虚函数不是虚函数)。
请记住:
operator=
返回一个 reference to *this
因为赋值支持连锁操作(采用右结合率),比如: a = b = c;
,所以重载赋值运算符必须返回一个指向操作数左侧对象的指针,也就是 *this
。
这不仅适用于标准赋值运算符,也适用于任何赋值相关的运算符重载,如 +=
、 -=
。
请记住:
reference to *this
operator=
中处理 “自我赋值”缘起于一个憨批操作:将自己赋给自己,即自我赋值。
一般不会有人这么写,但有些时候会比较隐蔽,比如:
a[i] = a[j]; // i == j *px = *py; // px == py
若有如下类(持有堆上内存的一个类):
class B {...}; class Widget { ... private: B* pb; }; Widget& Widget::operator=(const Widget& rhs) { delete pb; // pb 是 Wdiget 类中的一个指针对象 pb = new B(*rhs.pb); return *this; }
这个代码中在自我赋值便是错的。
即判断是不是自我赋值,如果是,不做任何事。
Widget& Widget::operator=(const Widget& rhs) { if (this == &rhs) return *this; delete pb; pb = new B(*rhs.pb); //如果此处抛出异常,pb将指向一块已经被删除的内存。 return *this; }
复制pb所指东西前不要删除pb:
Widget& Widget::operator=(const Widget& rhs) { B* temp = pb; pb = new B(*rhs.pb); // 此处可能抛出异常 delete temp; return *this; }
使用交换技术代替赋值(异常安全性更好)。
Copy and Swap技术:为你打算修改的对象做出一份副本,然后在副本上做一切必要的修改。若有任何修改动作抛出异常,原对象任保持未改变状态;待所有改变都成功后,再将修改过的那个副本与原对象在一个不抛出异常的操作中交换(swap)。
class Widget { ... void swap(Widget& rhs); // 用于交换 rhs 和 *this 的数据 ... }; //此方法更好 Widget& Widget::operator=(const Widget& rhs) { // 显式调用拷贝构造函数 Widget temp(rhs); // 仍然需要临时变量 swap(temp); return *this; } // 此方法过于隐晦 // 隐含了一个拷贝动作 Widget& Widget::operator=(Widget rhs) // 另一种变体,传值方式会复制一份副本 { swap(rhs); // ths 本身是副本,直接交换 return *this; }
请记住:
operator=
有良好行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及 copy-and-swap自定义了构造函数和 copy 函数,随后又新增了成员变量,那么需要自己留意在所有这些构造函数和 copy 函数中添加这个新的成员变量的操作,编译器不会提醒你。一旦忘记了,初始化或赋值操作就是不完整的。
如果是一个派生类中的 copy 函数,除了处理好派生类内的成员对象的 copy 操作,还要负责基类中对象的 copy 操作,也就是在初始化列表中完成对基类的 copy 操作。如下代码:
class Derived : public Base { public: ... Derived(const Derived& rhs); Derived& operator=(const Derived& rhs); private: int p; }; Derived::Derived(const Derived& rhs) : Base(rhs), // 注意这里,手动调用基类的 copy 构造函数 p(rhs.p) { ... } Derived& Derived::operator=(const Derived& rhs) { ... Base::operator=(rhs); // 注意这里,手动调用基类的 copy 赋值运算符函数 p = rhs.p; return *this; }
相同代码可以抽象为一个成员函数init();
请记住:
[1] Effective C++ · Scott Meyers
[2] 重述Effective C++