条款18:让接口容易被正确使用,不易被误用
请记住:
1、好的接口很容易被正确使用,不易被误用。你应该在你的所有接口中努力达成这些性质;
2、“促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容;
3、“阻止误用”的办法包括建立新类型、限制类型上的操作,束缚对象值,以及消除客户的资源管理责任;
4、tr1::shared_ptr 支持定制删除器。这可防范 DLL 问题,可被用来自动解除互斥锁等等。
条款19:设计 class 犹如设计 type
请记住:Class 的设计就是 type 的设计。在定义一个新的 type 之前,请确定你已经考虑过本条款覆盖的所有讨论主题。
条款20:宁以 pass-by-reference-to-const 替换 pass-by-value
pass-by-value 除了会有重新构造的代价外,还存在对象切割的问题。即当一个 derived 对象以 by value 方式传递给 base 对象时,会被视为一个 base 对象, derived 的构造函数并不会被调用,被调用的是 base 的构造函数。
举例如下:
<span style="font-size:14px;">#include <iostream>
using namespace std;
class base
{
public:
base(){cout << "base()" << endl;}
base(const base& b){cout << "copy base()" << endl;}
~base(){cout << "~base()" << endl;}
};
class derived: public base
{
public:
derived(){cout << "derived()" << endl;}
derived(const derived& d){cout << "copy derived()" << endl;}
~derived(){cout << "~derived()" << endl;}
};
void fun(base b)
{
return;
}
int main()
{
derived d;
fun(d);
return 0;
}</span>
结果如下:
<span style="font-size:14px;">base()
derived()
copy base()
~base()
~derived()
~base()
</span>
这里有个小插曲,为什么拷贝构造函数入参是 pass-by-reference-to-const?
如果是 pass-by-value 的话,由于 pass-by-value 本身的入参方式就需要调用拷贝构造函数,于是会陷入无穷的递归当中,这是编译器所不允许的。
请记住:
1、尽量以 pass-by-reference-to-const 替换 pass-by-value。前者通常比较高效,并且可避免切割问题(slicing problem)。
2、以上规则并不适用于内置类型,以及 STL 的迭代器和函数对象。对它们而言,pass-by-value 往往比较适当。
条款21:必须返回对象时,别妄想返回其 reference
对于一个函数,如果其必须返回对象时,别妄想返回其引用,这里我们可以简单分析一下返回 reference 的后果:
1、对于 stack based 变量:
base& fun()
{
base tmp;
return tmp;
}
返回的结果在函数退出前就被销毁了,返回值如今已是一个败坏的残骸。
2、对于 heap based 变量:
base& fun()
{
base *pb = new base;
return *pb;
}
分配在堆区的变量,试问谁来对其进行 delete 操作?很容造成内存泄露。
3、以上两种使用方式非常不好,有人于是想出“让 operator* 返回的 reference 指向一个被定义在函数内部的 static base 对象 ”:
const base& operator*(const base& b1, const base& b2)
{
static base result;
result = ...;
return result;
}
对于 static 对象,其不可避免的遭受多线程安全性的怀疑,不过在那之前,还有更容易被忽视的安全性:
bool operator==(const base& b1, const base& b2){}; // 一个针对 base 而写的 operator==
base b1, b2, b3, b4;
...
if((b1 * b2) == (b3 * b4))
{...}
else
{...}
在你执行之后会发现,此代码永远执行是 true 分支。那是因为 operator* 函数里的 static 对象只有一个,operator* 后所有对象是该对象的引用,导致operator== 执行的其实是 (static 对象 == static 对象) 的操作。
请记住:绝对不要返回 pointer 或 reference 指向一个 local stack 对象,或返回 reference 指向一个 heap-allocated 对象,或返回 pointer 或 reference 指向一个 local static 对象而有可能同时需要多个这样的对象。