第一个细节:
- 对于单纯常量,最好以 const 对象或 enum 替换 #define;
- 对于形式函数的宏,最好改用 inline 函数替换 #define。
第二个细节:
- 尽量的使用const
第三个细节:
- 构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列次序应该和它们在 class 中的声明次序(顺序)相同
第四个细节:
- 编译器可以暗自为 class 创建默认构造函数(如果没有声明任何构造函数)、复制构造函数、赋值操作符函数,以及析构函数
- 编译器拒绝为 class 创建 operator= 函数情况:(1) 内含引用的成员、(2) 内含 const 的成员、(3)基类将 operator= 函数声明为 private
第五个细节:
- 如果不想编译器自动生成函数,可将相应的成员函数声明为 private 并且不予实现(或者函数加=delete)。使用像 Uncopyale 这样的基类也是一种做法(继承私有)
第六个细节:
- 如果产生多态性质,基类的析构函数必须加virtual关键字,如果没有多态性质的基类,不要加virtual关键字
第七个细节:
- 在构造和析构期间不要调用 virtual,因为这类调用不会下降至派生类
第八个细节:
class A
{
public:
...
A& operator=(const A& rhs) // 返回类型是一个引用,指向当前对象。
{
...
return *this; // 返回左侧对象
}
...
};
- 令赋值操作符返回一个 reference to *this
第九个细节:
自我赋值
-
a[i] = a[j]; // 潜在的自我赋值
-
*px = *py; // 潜在自我赋值
-
这里的自我赋值的问题是, operator= 函数内的 *this(赋值的目的端)和 rhs 有可能是同一个对象。果真如此 delete 就不只是销毁当前对象的 pb,它也销毁 rhs 的 pb
A& A::operator=(const A& rhs) // 一份不安全的operator = 实现版本 { delete pb; // 释放旧的指针对象 pb = new B(*rhs.pb); // 生成新的地址 return *this; }
规避:
-
传统的做法是在
operator=
函数最前面加一个if
判断,判断是否是自己,不是才进行赋值操作 -
高效的方式使用所谓的 copy and swap 技术:
class A { ... void swap(A& rhs) // 交换*this 和 rhs 的数据 { using std::swap; swap(pb, rhs.pb); } ... private: B * pb; // 指针,指向一个从堆分配而得的对象 } }; A& A::operator=(const A& rhs) { A temp(rhs); // 为 rhs 制作一份复件(副本) swap(tmp); // 将 *this 数据和上述复件的数据交换。 return *this; }
当类里
operator=
函数被声明为「以 by value 方式接受实参」,那么由于 by value 方式传递东西会造成一份复件(副本),则直接 swap 交换即可,如下:A& A::operator=(A rhs) // rhs是被传对象的一份复件 { swap(rhs); // 将 *this 数据和复件的数据交换。 return *this; }
第十个细节:
- 复制对象时别忘了复制所有的成员,包括基类中的(少了复制成员,编译器不会告知)
本文由 Ryan 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为:
2020/05/11 11:41