读《Effective C++》知识点整理
背景:
只要定义了一个变量而其类型带有一个构造函数或析构函数,那么程序的控制流到达这个变量定义式时,你便得承受构造成本。
当这个变量离开作用域时,你便得承受析构成本。即使这个变量最终并未被使用,仍需考虑这些成本。因此你必须尽可能避免这情况
示例1:
std::string encryptpassword(const std::string& password)
{
using namespace std;
string encrypted;
if(password.length() < 10)
{
throw logic_error("Password is too short");
}
...
return encrypted;
}
encrypted这个变量未完全使用。但必须承担一次构造成本和析构成本。
优化1:
std::string encryptpassword(const std::string& password)
{
using namespace std;
if(password.length() < 10)
{
throw logic_error("Password is too short");
}
string encrypted;
....
return encrypted;
}
encrypted这个变量完全使用。但程序抛出异常却不必承担一次构造成本和析构成本。
存在的问题:
encrypted虽然定义却无任何实参作初值。
意味:encrypted调用的是default构造函数
建议:直接在构造时指定初始值,比通过默认构造函数构造出一个对象之后,再对它赋值的效率高。
承上,优化2:
std::string encryptpassword(const std::string& password)
{
using namespace std;
if(password.length() < 10)
{
throw logic_error("Password is too short");
}
std::string encrypt(password);
string encrypted(encrypt);
....
return encrypted;
}
结论1:尽量延后,不只是延后变量的定义,直到非用不可为止,还应该延后这份定义直到能给它初值实参为止。
优点:不仅能避免构造和析构非必要对象,还可以避免无意义的默认构造行为。
更深一层,使用明显具有意义的初值将变量初始化,还可以附带说明变量的目的。
示例2:
//方法A:string对象定义循环外
string S;
for (int i = 0; i< n; ++i)
{
S = 根据 i 得到的某个值;
}
//方法B:string对象定义循环内
for (int i = 0; i< n; ++i)
{
string S(根据 i 得到的某个值);
}
针对string对象的使用成本:
方法A:1个构造函数+1个析构函数+n个赋值操作;
方法B:n个构造函数+n个析构函数
谁的成本大?
答:如果class的一个赋值成本低于一组构造+析构成本,方法A整体而言比较高效;尤其当n很大的时候,效果明显。反之,方法B更好。
方法A的使用,要求:
1)明确赋值成本低于构造+析构成本;2)明确当前处理的逻辑/业务对效率高度敏感。
否则,使用方法B。