一、变量定义的成本
- 只要你定义一个变量,该变量带有构造函数与析构函数,那么:
- 当你定义这个变量时就要执行它的构造函数
- 变量生命周期结束后就要执行他的析构函数
二、尽量延迟变量定义的时间
- 变量定义需要执行构造与析构函数,因此在某些情况下为了提高程序的效率,应该延迟变量定义的时间。请看下面的演示案例:
演示案例
- 下面定义一个函数,用来对密码进行加密,如果参数传入的密码过段就抛出一个logic_error异常(见条款54)
//参数:要加密的密码 //返回值:加密后的密码 std::string encryptPassword(const std::string& password) { using namespace std; string encrypted; //定义的临时变量 if (password.length()<MinimumPasswordLength) { throw logic_error("Password is too short"); } //... return encrypted; }
- 上面我们定义了一个临时变量encrypted,但是如果函数执行到if的时候程序抛出了异常,那么你就需要承担encrypted变量的构造与析构函数(但是我们没有完全用到这个变量),因此为了提升效率,我们应该延迟变量encrypted的定义
//参数:要加密的密码 //返回值:加密后的密码 std::string encryptPassword(const std::string& password) { using namespace std; if (password.length()<MinimumPasswordLength) { throw logic_error("Password is too short"); } string encrypted; //在异常的后面定义 //... return encrypted; }
三、以直接构造代替变量的默认构造函数
- 上面我们的变量encrypted使用的是默认的构造函数,在条款4中我们说过“通过默认的构造函数构造对象”比“直接在构造时指定初值”的效率差。因此我们应该避免变量定义时使用默认的构造函数
演示案例
- 下面我们定义的变量encrypted使用默认构造函数,然后再以拷贝辅助运算符将参数赋值给它
//参数:要加密的密码 //返回值:加密后的密码 std::string encryptPassword(const std::string& password) { using namespace std; if (password.length()<MinimumPasswordLength) { throw logic_error("Password is too short"); } string encrypted; //使用默认的构造函数 encrypted=password; //然后再使用拷贝赋值运算符进行赋值 //... return encrypted; }
- 上面的效率比较低, 因此我们建议在变量定义时直接构造
//参数:要加密的密码 //返回值:加密后的密码 std::string encryptPassword(const std::string& password) { using namespace std; if (password.length()<MinimumPasswordLength) { throw logic_error("Password is too short"); } string encrypted(password); //使用拷贝构造函数 //... return encrypted; }
四、有循环时变量何时定义?
- 例如下面是将变量定义在循环外以及循环内的情况
//方法A:定义于循环外
Widget w;
for (int i = 0; i < n; ++i) {
w = 取决于i的某个值;
}
//方法B:定义于循环内
for (int i = 0; i < n; ++i) {
Widget w = 取决于i的某个值;
}
- 对于Widget来说,上面付出的代价有:
- 做法A:1个构造函数+1个析构函数+n个赋值操作
- 做法B:n个构造函数+n个析构函数
- 如果类的赋值成本低于一组构造+析构,那么做法A可选,尤其当n值很大的时候;否则做法B比较好
- 做法A中w的作用域更大,如果与程序的可理解性或易维护性造成冲突的话,选择B比较好
五、总结
- 尽可能延后变量定义式的出现。这样做可增加程序的清晰度并改善程序效率