Effective c++之Item 26: 只要有可能就推迟变量定义
来源:互联网 发布:数据 陈中源 编辑:程序博客网 时间:2024/05/16 07:16
不仅仅是需要变量的时候才定义,有时候也要看具体的效率和代价,真正的精髓在“只要有可能“这两个字上。
只要你定义了一个带有构造函数和析构函数的类型的变量,当控制流程到达变量定义的时候会使你担负构造成本,而当变量离开作用域的时候会使你担负析构成本。如果有无用变量造成这一成本,你就要尽你所能去避免它。
你可能认为你从来不会定义无用的变量,但是也许你应该再想一想。考虑下面这个函数,只要 password 的长度满足要求,它就返回一个 password 的加密版本。如果 password 太短,函数就会抛出一个定义在标准 C++ 库中的 logic_error 类型的异常(参见 Item 54):
// 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 在这个函数中并不是完全无用,但是如果抛出了一个异常,它就是无用的。换句话说,即使 encryptPassword 抛出一个异常,你也要为构造和析构 encrypted 付出代价。因此得出以下结论:你最好将 encrypted 的定义推迟到你确信你真的需要它的时候:
// 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 的时候没有任何初始化参数。这就意味着很多情况下将使用它的缺省构造函数,对于一个对象你首先应该做的就是给它一些值,这经常可以通过赋值来完成。Item 4 解释了为什么缺省构造(default-constructing)一个对象然后赋值给它比用你真正需要它持有的值初始化它更低效。那个分析也适用于此。例如,假设 encryptPassword 的核心部分是在这个函数中完成的:
void encrypt(std::string& s); // encrypts s in place
那么,encryptPassword 就可以这样实现,即使它还不是最好的方法:
// this function postpones encrypted's definition until
// it's necessary, but it's still needlessly inefficient
std::string encryptPassword(const std::string& password)
{
... // check length as above
std::string encrypted; // default-construct encrypted
encrypted = password; // assign to encrypted
encrypt(encrypted);
return encrypted;
}
一个更可取得方法是用 password 初始化 encrypted,从而跳过毫无意义并可能很昂贵的缺省构造:
// finally, the best way to define and initialize encrypted
std::string encryptPassword(const std::string& password)
{
... // check length
std::string encrypted(password); // define and initialize
// via copy constructor
encrypt(encrypted);
return encrypted;
}
这个建议就是本 Item 的标题中的“只要有可能(as long as possible)”的真正含义。你不仅应该推迟一个变量的定义直到你不得不用它之前的最后一刻,而且应该试图推迟它的定义直到你得到了它的初始化参数。通过这样的做法,你可以避免构造和析构无用对象,而且还可以避免不必要的缺省构造。更进一步,通过在它们的含义已经非常明确的上下文中初始化它们,有助于对变量的作用文档化。
“但是对于循环会如何?”你可能会有这样的疑问。如果一个变量仅仅在一个循环内使用,是循环外面定义它并在每次循环迭代时赋值给它更好一些,还是在循环内部定义这个变量更好一些呢?也就是说,下面这两个大致的结构中哪个更好一些?
// Approach A: define outside loop //Approach B: define inside loop
Widget w;
for (int i = 0; i < n; ++i){ for (int i = 0; i < n; ++i) {
w =some value dependent on i;Widget w(some value dependent on i);
... ...
} }
这里我将一个类型 string 的对象换成了一个类型 Widget 的对象,以避免对这个对象的构造、析构或赋值操作的成本的任何已有的预见。
对于 Widget 的操作而言,就是下面这两个方法的成本:
· 方法 A:1 个构造函数 + 1 个析构函数 + n 个赋值。
· 方法 B:n 个构造函数 + n 个析构函数。
对于那些赋值的成本低于一个构造函数/析构函数对的成本的类,方法 A 通常更高效。特别是在 n 变得很大的情况下。否则,方法 B 可能更好一些。此外,方法 A 与方法 B 相比,使得名字 w 在一个较大的区域(包含循环的那个区域)内均可见,这可能会破坏程序的易理解性和可维护性。因此得出以下结论:除非你确信以下两点:(1)赋值比构造函数/析构函数对成本更低,而且(2)你正在涉及你的代码中的性能敏感的部分,否则,你应该默认使用方法 B。
Things to Remember
· 只要有可能就推迟变量定义。这样可以增加程序的清晰度并提高程序的性能。
- Effective c++之Item 26: 只要有可能就推迟变量定义
- [翻译] Effective C++, 3rd Edition, Item 26: 只要有可能就推迟 variable definitions(变量定义)
- 只要有可能,就推迟变量定义
- 只要有可能就推迟 variable definitions(变量定义)
- C++箴言:只要有可能就推迟变量定义
- Item 26 推迟变量的定义
- Item 26:为什么要推迟变量的定义? Effective C++笔记
- Item 15: 只要有可能,就使用constexpr
- Item 15: 只要有可能,就使用constexpr
- [翻译] Effective C++, 3rd Edition, Item 3: 只要可能就用 const
- 变量的推迟定义
- Effective C++ Item 26 尽可能延后变量定义式的出现时间
- Effective C++ 第二版 31)局部对象引用和函数内new的指针 32)推迟变量定义
- 尽可能地推迟变量的定义
- 《Effective C#》Item 2:定义常量的两种方法
- 《Effective C#》Item 2:定义常量的两种方法
- 《Effective C#》Item 22:用事件定义对外接口
- 《more effective c++》Item M5:谨慎定义类型转换函数
- @RequestParam 和 @ PathVariable 的区别
- html5页面中打开本地app,如果没有跳转下载页面的解决方案
- ArrayList源码(1)
- Intellij学习笔记 --- 更改maven本地路径和外部仓库地址
- QML 学习笔记
- Effective c++之Item 26: 只要有可能就推迟变量定义
- PHP-变量(一)(变量赋值、判断变量是否为空,变量删除,可变变量等)
- 排序相关——二维数组中的查找
- Dlib人脸检测及关键点描述的python实现
- 设计模式原则(4):接口隔离原则
- 20170713
- 如何拆分你的微服务架构?
- Ajax技术---Ajax技术原理,自己写一个Ajax
- reids centos7 安装