0.概述
尽可能延后变量定义式的出现。这样做可增加程序的清晰度并改善程序效率。
延后的真正含义有2:
- 延后变量的定义,直到非得使用该变量的前一刻为止;
- 延后变量的定义,直到能够给它初值实参为止。
1. 第一层含义
1.1 不推荐做法举例
只要你定义了一个变量而其类型带有一个构造函数或析构函数,那么当程序的控制流(control flow)到达这个变量定义式时,你便得承受构造成本;当这个变量离开其作用域时,你便得承受析构成本。即使这个变量最终并未被使用,仍需耗费这些成本,所以应该尽可能避免这种情形。
// this function defines the variable "encrypted" too soon
std::string encryptPassword(const std::string& password)
{
using namespace std;
string encrypted;
if (password.length() < MinimumPasswordLength) {
throw logic_error("Password is too short");
}
... // do whatever is necessary to place an
// encrypted version of password in encrypted
return encrypted;
}
过早定义encrypted,如果抛出异常,就不会被使用,但是依然付出了构造和析构成本
1.2 推荐做法举例
// this function postpones encrypted’s definition until it’s truly necessary
std::string encryptPassword(const std::string& password)
{
using namespace std;
if (password.length() < MinimumPasswordLength) {
throw logic_error("Password is too short");
}
string encrypted;
... // do whatever is necessary to place an
// encrypted version of password in encrypted
return encrypted;
}
延后encrypted的定义直到真的使用它
2.第二层含义
1.2中的代码虽然有定义但却没有任何实参作为初值。
条款4曾解释为什么“通过default构造函数构造出一个对象然后对它赋值”比“直接在构造时指定初值”效率差。也适用于这里
2.1 不推荐做法举例
// this function postpones encrypted’s definition until
// it’s necessary, but it’s still needlessly inefficient
std::string encryptPassword(const std::string& password)
{
... // import std and check length as above
string encrypted; // default-construct encrypted
encrypted = password; // assign to encrypted
encrypt(encrypted);
return encrypted;
}
2.2 推荐做法举例
以password作为encrypted的初值,跳过无意义的默认构造过程:
// finally, the best way to define and initialize encrypted
std::string encryptPassword(const std::string& password)
{
... // import std and check length
string encrypted(password); // define and initialize via copy
// constructor
encrypt(encrypted);
return encrypted;
}
3. 循环怎么办?
下面有两种可能的做法:
planA:
//方法A:定义于循环外
Widget w;
for (int i = 0; i < n; ++i){
w=取决于i的某个值;
}
planB:
//方法B:定义于循环内
for (int i = 0; i < n; ++i){
widget w(取决于i的某个值);
}
以上两种写法的成本如下:
- 做法A:1个构造函数+1个析构函数+n个赋值操作
- 做法B: n个构造函数+n个析构函数
如果classes的一个赋值成本低于一组构造+析构成本,做法A大体而言比较高效。尤其当n值很大的时候。否则做法B或许较好。
此外做法A造成名称w的作用域(覆盖整个循环)比做法B更大,有时对程序的可理解性和易维护性造成冲突。
总结:除非(1)知道赋值成本比“构造+析构”成本低,(2)你正在处理代码中效率高度敏感(performance-sensitive)的部分,否则应该使用做法B。