读书笔记 effective c++ Item 26 尽量推迟变量的定义
1. 定义变量会引发构造和析构开销
每当你定义一种类型的变量时:当控制流到达变量的定义点时,你引入了调用构造函数的开销,当离开变量的作用域之后,你引入了调用析构函数的开销。对未使用到的变量同样会产生开销,因此对这种定义要尽可能的避免。
2. 普通函数中的变量定义推迟
2.1 变量有可能不会被使用到的例子
你可能会想你永远不会定义未使用的变量,你可能要再考虑考虑。看下面的函数,此函数返回password的加密版本,提供的password需要足够长。如果password太短,函数会抛出一个logic_error类型的异常,此异常被定义在标准C++库中(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的构造函数和析构函数买单。因此,最好推迟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;
}
2.2 推迟变量定义的一种方法
上面的代码看起来还是不够紧凑,因为encrypted定义时没有带任何初始化参数。也就意味着默认构造函数会被调用。在许多情况下,你对一个对象做的第一件事就是给它提供一些值,这通常通过赋值来进行。Item 4解释了为什么默认构造一个对象紧接着对其进行赋值要比用一个值对其初始化效率要低。其中的分析在这里同样适用。举个例子,假设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)
{
... // import std and check length as above
string encrypted; // default-construct encrypted
encrypted = password; // assign to encrypted
encrypt(encrypted);
return encrypted;
}
2.2 推迟变量定义的更好方法
一个更好的方法是用password来初始化encypted,这样就跳过了无意义的和可能昂贵的默认构造函数:
// 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;
}
2.3 推迟变量定义的真正含义
这个建议是这个条款的标题中的“尽量推迟”的真正含义。你不但要将变量的定义推迟到你必须使用的时候,你同样应该尝试将定义推迟到你获得变量的初始化值的时候。这么做,你就能避免不必要的构造和析构,也避免了不必要的默认构造函数。并且,通过在意义已经明确的上下文中对变量进行初始化,你也帮助指明了使用此变量的意图。
3. 如何处理循环中的变量定义
这时候你该想了:循环该怎么处理呢?如果一个变量只在一个循环中被使用,是将将变量定义在循环外,每次循环迭代为其赋值好呢?还是将其定义在循环内部好呢?也即是下面的结构哪个好?
// Approach A: define outside loop
Widget w;
for (int i = ; i < n; ++i) {
w = some value dependent on i;
...
}
// Approach B: define inside loop
for (int i = ; i < n; ++i) {
Widget w(some value dependent oni);
...
}
这里我用一个Widget类型的对象来替换string类型的对象,以避免对执行构造函数,析构函数或者赋值运算符的开销有任何偏见。
对于Widget来说,两种方法的开销如下:
- 方法一: 1个构造函数+1个析构函数+n个赋值运算
- 方法二:n个构造函数和n个析构函数
如果赋值运算的开销比一对构造函数/析构函数要小,方法A更加高效。尤其是在n很大的时候。否则,方法B要更高效。并且方法A比方法B使变量w在更大的范围内可见,这一点违反了程序的可理解性和可操作性。因此,除非你遇到下面两点:(1)赋值比构造/析构开销要小(2)你正在处理对性能敏感的代码。否则你应该默认使用方法B。
读书笔记 effective c++ Item 26 尽量推迟变量的定义的更多相关文章
- 读书笔记 effective c++ Item 27 尽量少使用转型(casting)
C++设计的规则是用来保证使类型相关的错误不再可能出现.理论上来说,如果你的程序能够很干净的通过编译,它就不会尝试在任何对象上执行任何不安全或无意义的操作.这个保证很有价值,不要轻易放弃它. 不幸的是 ...
- 读书笔记 effective c++ Item 2 尽量使用const,枚举(enums),内联(inlines),不要使用宏定义(define)
这个条目叫做,尽量使用编译器而不要使用预处理器更好.#define并没有当作语言本身的一部分. 例如下面的例子: #define ASPECT_RATIO 1.653 符号名称永远不会被编译器看到.它 ...
- 读书笔记 effective c++ Item 18 使接口容易被正确使用,不容易被误用
1. 什么样的接口才是好的接口 C++中充斥着接口:函数接口,类接口,模板接口.每个接口都是客户同你的代码进行交互的一种方法.假设你正在面对的是一些“讲道理”的人员,这些客户尝试把工作做好,他们希望能 ...
- 读书笔记 effective c++ Item 24 如果函数的所有参数都需要类型转换,将其声明成非成员函数
1. 将需要隐式类型转换的函数声明为成员函数会出现问题 使类支持隐式转换是一个坏的想法.当然也有例外的情况,最常见的一个例子就是数值类型.举个例子,如果你设计一个表示有理数的类,允许从整型到有理数的隐 ...
- 读书笔记 effective c++ Item 49 理解new-handler的行为
1. new-handler介绍 当操作符new不能满足内存分配请求的时候,它就会抛出异常.很久之前,它会返回一个null指针,一些旧的编译器仍然会这么做.你仍然会看到这种旧行为,但是我会把关于它的讨 ...
- 读书笔记 effective c++ Item 54 让你自己熟悉包括TR1在内的标准库
1. C++0x的历史渊源 C++标准——也就是定义语言的文档和程序库——在1998被批准.在2003年,一个小的“修复bug”版本被发布.然而标准委员会仍然在继续他们的工作,一个“2.0版本”的C+ ...
- 读书笔记 effective c++ Item 1 将c++视为一个语言联邦
Item 1 将c++视为一个语言联邦 如今的c++已经是一个多重泛型变成语言.支持过程化,面向对象,函数式,泛型和元编程的组合.这种强大使得c++无可匹敌,却也带来了一些问题.所有“合适的”规则看上 ...
- 读书笔记 effective c++ Item 9 绝不要在构造函数或者析构函数中调用虚函数
关于构造函数的一个违反直觉的行为 我会以重复标题开始:你不应该在构造或者析构的过程中调用虚函数,因为这些调用的结果会和你想的不一样.如果你同时是一个java或者c#程序员,那么请着重注意这个条款,因为 ...
- 读书笔记 effective c++ Item 11 在operator=中处理自我赋值
1.自我赋值是如何发生的 当一个对象委派给自己的时候,自我赋值就会发生: class Widget { ... }; Widget w; ... w = w; // assignment to sel ...
随机推荐
- [UT]Unit Test理解
Coding中有一个原则:Test Driven Development. UT中的一些基本概念: 1. 测试驱动 2. 测试桩 3. 测试覆盖 4. 覆盖率 单体测试内容: 1. 模块接口:测试模 ...
- mui中文在线手册及教程文档
http://dev.dcloud.net.cn/mui/ui/index.html#mask http://ask.dcloud.net.cn/question/2403 http://ask.dc ...
- 2.5. Integer 16 、Integer 32、Integer 64(Core Data 应用程序实践指南)
Core Data 使用 “带符号的整数”,通常我们会选择Integer 32,如果不够,可以升级为Integer 64 (第3章),通过升级托管对象模型. 这三种类型对应的特性(Property)类 ...
- Android Studio快捷键汇总
- win8系统换win7系统
吐槽一下先,win8换win7好费事~第一次弄,不过总算弄好了,记录一下吧. 首先,最坑人的就是,win8没法像win7那样按F1或者别的,直接进入BIOS,也就没法设置U盘引导,据说是由于win8的 ...
- 关于多字节字符入库失败处理(所谓的Emji),该处理是舍弃特殊字符
具体处理方法及样例如下: /** * 屏蔽超过三个字节以上的字符 * @param strByte * @return */ public static String filterUtf8(byte[ ...
- win10环境下python3.5安装步骤
点我去Python官网下载 往下翻几页就能看到各种版本的Python,当前最新的是Python3.6,也没多大区别,我选择的是3.5.2 64位的,点击download 根据自己的电脑配置,我选择的是 ...
- 《连载 | 物联网框架ServerSuperIO教程》- 15.数据持久化接口的使用。附:3.2发布与版本更新说明。
1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...
- 下一个项目为什么要用 SLF4J
阿里巴巴 Java 开发手册 前几天阿里巴巴在云栖社区首次公开阿里官方Java代码规范标准,就是一个PDF手册,有命名规范,让你知道自己原来取的每一个类名.变量名都是烂名字,真替你家未来孩子担心:有集 ...
- linux虚拟机CentOS 7完整安装流程截图
安装VMware虚拟机过程此处省略,只介绍在虚拟机上安装linux系统CentOS 7过程截图. 1 新建虚拟机 2 命名虚拟机,选择linux安装位置 3 选择虚拟机处理器数量和处理器核心数 4 分 ...