条款25:考虑写出一个不抛出异常的swap
swap是个有趣的函数。原本他只是STL的一部分。而后成为异常安全性编程(见条款29)的脊柱,以及用来处理自我赋值可能性(见条款11)的一个常见机制。STL中swap的实现如下:
namespace std {
template<typename T>
void swap(T& a, T& b) {
T temp(a);
a = b;
b = temp;
}
}
上述代码要求类型T支持拷贝构造 和赋值操作符。该缺省swap需要执行1次拷贝构造和2次赋值操作,有时候速度较慢。
以pimpl手法设计Widget class,如下所示:
class Widget {
public:
Widget(const Widget& rhs);
Widget& operator=(const Widget& rhs) {
...
*pImpl = *(rhs.pImpl);
}
private:
WidgetImpl* pImpl; //指针,所指对象内含Widget数据。
//WidgetImpl是针对Widget class,
//设计的class,包含很多数据成员。
};
一旦要置换两个Widget对象值,我们只需要置换其pImpl指针,但缺省的swap算法不知道这点,它不只复制三个Widget对象,还复制三个WidgetImpl对象。非常缺乏效率。
我们令Widget声明一个名为swap的public成员函数做真正的置换工作,然后将std::swap特化,令它调用该成员函数:
class Widget {
public:
...
void swap(Widget& other) {
using std::wap; //稍后解释
swap(pImpl, other.pImpl); //置换pImpl指针
}
...
};
namespace std {
template<>
void swap<Widget>(Widget& a, Widget& b) {
a.swap(b); //调用swap成员函数
}
}
这种做法不只可以通过编译,还与STL容器有一致性,因为所有STL容器也都提供有public swap成员函数和std::swap特化版本。
略(后续)。。。
条款26:尽可能延后变量定义式的出现时间
假设有这样一个函数,它计算通行密码的加密版本而后返回,前提是密码够长。如果密码太短,函数会丢出一个异常:
string encryptPassword(const string& password) {
using namespace std;
string encrypted;
if(password.length() , MinimumPasswordLength) {
throw logic_error("Password is too short");
}
...
return encrypted;
}
对象encrypted在此函数中并非完全未被使用,但如果有个异常被丢出,它就真的没被使用。也就是说如果函数encryptPassword丢出异常,你仍得付出encrypted的构造成本和析构成本。所以延后定义会更好。
string encryptPassword(const string& password) {
using namespace std;
if(password.length() , MinimumPasswordLength) {
throw logic_error("Password is too short");
}
string encrypted; //延后定义
...
return encrypted;
}
上述encrypted调用了default构造函数和赋值操作符,如果直接对其进行copy构造则效果更好。
变量是应该定义在循环内还是在循环外定义,然后每次循环赋值?两种写法的成本如下:
扫描二维码关注公众号,回复:
10163516 查看本文章
- 做法A在循环外:1个构造函数+1个析构函数+n个赋值操作
- 做法B在循环内 :n个构造函数+n个析构函数
如果对象的一次赋值操作成本大于一次构造和析构的成本之和,那么选用第二种方式,否则选择第一种。但是除非(1)你知道赋值成本比“构造+析构“成本低,(2)你正在处理代码中效率高度敏感的部分,否则你应该使用B做法。