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

小编 2026-06-22 阅读:1846 评论:0
1. 定义变量会引发构造和析构开销每当你定义一种类型的变量时:当控制流到达变量的定义点时,你引入...

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 an18 19 // encrypted version of password in encrypted20 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 an18 19 // encrypted version of password in encrypted20 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 above10 11 string encrypted; // default-construct encrypted12 13 encrypted = password; // assign to encrypted14 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 copy10 11 // constructor12 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 loop16 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。


作者:HarlanC

博客地址:http://www.cnblogs.com/harlanc/
个人博客: http://www.harlancn.me/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接

如果觉的博主写的可以,收到您的赞会是很大的动力,如果您觉的不好,您可以投反对票,但麻烦您留言写下问题在哪里,这样才能共同进步。谢谢!

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表