1. 定义变量会引发构造和析构开销

每当你定义一种类型的变量时:当控制流到达变量的定义点时,你引入了调用构造函数的开销,当离开变量的作用域之后,你引入了调用析构函数的开销。对未使用到的变量同样会产生开销,因此对这种定义要尽可能的避免。

2. 普通函数中的变量定义推迟

2.1 变量有可能不会被使用到的例子

你可能会想你永远不会定义未使用的变量,你可能要再考虑考虑。看下面的函数,此函数返回password的加密版本,提供的password需要足够长。如果password太短,函数会抛出一个logic_error类型的异常,此异常被定义在标准C++库中(Item 54):

 1 // this function defines the variable "encrypted" too soon
 2
 3 std::string encryptPassword(const std::string& password)
 4
 5 {
 6
 7 using namespace std;
 8
 9 string encrypted;
10
11 if (password.length() < MinimumPasswordLength) {
12
13 throw logic_error("Password is too short");
14
15 }
16
17 ... // do whatever is necessary to place an
18
19 // encrypted version of password in encrypted
20
21 return encrypted;
22
23 }

对象encrypted不是完全不会被用到,但是如果抛出了异常它就肯定不会被用到。这就是说,如果encryptPassword抛出了异常,你不会用到encrypted,但是你同样会为encrypted的构造函数和析构函数买单。因此,最好推迟encrypted的定义直到你认为你会使用它:

 1 // this function postpones encrypted’s definition until it’s truly necessary
 2
 3 std::string encryptPassword(const std::string& password)
 4
 5 {
 6
 7 using namespace std;
 8
 9 if (password.length() < MinimumPasswordLength) {
10
11 throw logic_error("Password is too short");
12
13 }
14
15 string encrypted;
16
17 ... // do whatever is necessary to place an
18
19 // encrypted version of password in encrypted
20
21 return encrypted;
22
23 }

2.2 推迟变量定义的一种方法

上面的代码看起来还是不够紧凑,因为encrypted定义时没有带任何初始化参数。也就意味着默认构造函数会被调用。在许多情况下,你对一个对象做的第一件事就是给它提供一些值,这通常通过赋值来进行。Item 4解释了为什么默认构造一个对象紧接着对其进行赋值要比用一个值对其初始化效率要低。其中的分析在这里同样适用。举个例子,假设encryptPassword函数的最困难的部分在下面的函数中执行:

1 void encrypt(std::string& s); // encrypts s in place

然后encryptPassword可以像下面这样实现,虽然这可能不是最好的方法:

 1 // this function postpones encrypted’s definition until
 2
 3 // it’s necessary, but it’s still needlessly inefficient
 4
 5 std::string encryptPassword(const std::string& password)
 6
 7 {
 8
 9 ... // import std and check length as above
10
11 string encrypted; // default-construct encrypted
12
13 encrypted = password; // assign to encrypted
14
15 encrypt(encrypted);
16
17 return encrypted;
18
19 }

2.2 推迟变量定义的更好方法

一个更好的方法是用password来初始化encypted,这样就跳过了无意义的和可能昂贵的默认构造函数:

 1 // finally, the best way to define and initialize encrypted
 2
 3 std::string encryptPassword(const std::string& password)
 4
 5 {
 6
 7 ... // import std and check length
 8
 9 string encrypted(password); // define and initialize via copy
10
11 // constructor
12
13 encrypt(encrypted);
14
15 return encrypted;
16
17 }

2.3 推迟变量定义的真正含义

这个建议是这个条款的标题中的“尽量推迟”的真正含义。你不但要将变量的定义推迟到你必须使用的时候,你同样应该尝试将定义推迟到你获得变量的初始化值的时候。这么做,你就能避免不必要的构造和析构,也避免了不必要的默认构造函数。并且,通过在意义已经明确的上下文中对变量进行初始化,你也帮助指明了使用此变量的意图

3. 如何处理循环中的变量定义

这时候你该想了:循环该怎么处理呢?如果一个变量只在一个循环中被使用,是将将变量定义在循环外,每次循环迭代为其赋值好呢?还是将其定义在循环内部好呢?也即是下面的结构哪个好?

 1 // Approach A: define outside loop
 2
 3 Widget w;
 4
 5 for (int i = 0; i < n; ++i) {
 6
 7 w = some value dependent on i;
 8
 9 ...
10
11 }
12
13
14
15 // Approach B: define inside loop
16
17 for (int i = 0; i < n; ++i) {
18
19 Widget w(some value dependent oni);
20
21 ...
22
23 }

这里我用一个Widget类型的对象来替换string类型的对象,以避免对执行构造函数,析构函数或者赋值运算符的开销有任何偏见。

对于Widget来说,两种方法的开销如下:

  • 方法一: 1个构造函数+1个析构函数+n个赋值运算
  • 方法二:n个构造函数和n个析构函数

如果赋值运算的开销比一对构造函数/析构函数要小,方法A更加高效。尤其是在n很大的时候。否则,方法B要更高效。并且方法A比方法B使变量w在更大的范围内可见,这一点违反了程序的可理解性和可操作性。因此,除非你遇到下面两点:(1)赋值比构造/析构开销要小(2)你正在处理对性能敏感的代码。否则你应该默认使用方法B。

转载于:https://www.cnblogs.com/harlanc/p/6498143.html

读书笔记 effective c++ Item 26 尽量推迟变量的定义相关推荐

  1. 读书笔记 effective c++ Item 18 使接口容易被正确使用,不容易被误用

    1. 什么样的接口才是好的接口 C++中充斥着接口:函数接口,类接口,模板接口.每个接口都是客户同你的代码进行交互的一种方法.假设你正在面对的是一些"讲道理"的人员,这些客户尝试把 ...

  2. 读书笔记 effective c++ Item 49 理解new-handler的行为

    1. new-handler介绍 当操作符new不能满足内存分配请求的时候,它就会抛出异常.很久之前,它会返回一个null指针,一些旧的编译器仍然会这么做.你仍然会看到这种旧行为,但是我会把关于它的讨 ...

  3. 读书笔记 effective c++ Item 50 了解何时替换new和delete 是有意义的

    1. 自定义new和delete的三个常见原因 我们先回顾一下基本原理.为什么人们一开始就想去替换编译器提供的operator new和operator delete版本?有三个最常见的原因: 为了检 ...

  4. 读书笔记 effective c++ Item 34 区分接口继承和实现继承

    看上去最为简单的(public)继承的概念由两个单独部分组成:函数接口的继承和函数模板继承.这两种继承之间的区别同本书介绍部分讨论的函数声明和函数定义之间的区别完全对应. 1. 类函数的三种实现 作为 ...

  5. 读书笔记 effective c++ Item 30 理解内联的里里外外 (大师入场啦)

    正文 最近北京房价蹭蹭猛涨,买了房子的人心花怒放,没买的人心惊肉跳,咬牙切齿,楼主作为北漂无房一族,着实又亚历山大了一把,这些天晚上睡觉总是很难入睡,即使入睡,也是浮梦连篇,即使亚历山大,对C++的热 ...

  6. 读书笔记 effective c++ Item 16 成对使用new和delete时要用相同的形式

    1. 一个错误释放内存的例子 下面的场景会有什么错? 1 std::string *stringArray = new std::string[100]; 2 3 ... 4 5 delete str ...

  7. 读书笔记 effective c++ Item 41 理解隐式接口和编译期多态

    1. 显示接口和运行时多态 面向对象编程的世界围绕着显式接口和运行时多态.举个例子,考虑下面的类(无意义的类), 1 class Widget { 2 public: 3 Widget(); 4 vi ...

  8. 读书笔记 effective c++ Item 47 使用traits class表示类型信息

    STL主要由为容器,迭代器和算法创建的模板组成,但是也有一些功能模板.其中之一叫做advance.Advance将一个指定的迭代器移动指定的距离: 1 template<typename Ite ...

  9. 读书笔记 effective c++ Item 5 了解c++默认生成并调用的函数

    1 编译器会默认生成哪些函数  什么时候空类不再是一个空类?答案是用c++处理的空类.如果你自己不声明,编译器会为你声明它们自己版本的拷贝构造函数,拷贝赋值运算符和析构函数,如果你一个构造函数都没有声 ...

  10. [读书笔记]Effective C++ - Scott Meyers

    [读书笔记]Effective C++ - Scott Meyers 条款01:视C++为一个语言联邦 C++四个次语言: 1. C Part-of-C++,没有模板.异常.重载. 2. Object ...

最新文章

  1. 3个CCIE对一个工程师的面试题(远去之路无比艰辛啊!)
  2. 有了我的第一个留言,高兴
  3. pytorch函数整理
  4. html 超链接打开Excel,计算机打开Excel超链接时提示的解决方案
  5. IIS 支持 php
  6. day006bootstrap的简单学习 + 轮播图
  7. Springboot集成BeanValidation扩展二:加载jar中的资源文件
  8. 不要卡巴斯基!(卡巴斯基授权许可文件出错,其它软件受连累)
  9. Xshell和secureCRT
  10. 眼镜商城开发,让眼镜世界更丰富
  11. 一名爱折腾人士的Apps for iPhone分享
  12. wps工资表怎么用计算机,wps制作工资表的方法步骤图
  13. 移动端 click 300ms 延迟,如何解决
  14. option样式美化 css,CSS select样式优化
  15. C# 委托、匿名委托、Lambda表达式详解
  16. 附录三 虚拟机的使用
  17. 正则表达式匹配居民身份证
  18. Source Insight 许可证激活与停用
  19. 猫狗大战----类的实例化和继承,带你看懂类方法的使用
  20. WT588F34B语音芯片单曲更换语音功能的实现与应用

热门文章

  1. java自动化静态代码检查_Jenkins+findbugs对java代码进行静态代码分析
  2. 处理得怎么样填空词语_武都网络推广软件效果怎么样【易商网络】
  3. 事务影响懒加载 no session or session was closed
  4. VMware vCenter Server安装与配置
  5. 高速缓存dns/ddns
  6. 《Ansible权威指南 》一1.6 Ansible应用场景
  7. java8 新特性实践
  8. Spark 概念学习系列之从物理执行的角度透视spark Job(十七)
  9. CentOS 7, apm+xcache, rpm包, php module
  10. jquery知识点总结(转)