今日是:
  小新技术网 -=> 程序开发 -=> C/C++区 -=> 正文

只要有可能就推迟变量定义

作者:[本站整理] 来源:[本站] 浏览:[] 评论:[]  【字体:
  在极大程度上,为你的类(包括类模板)和函数(包括函数模板)提供正确的定义是战斗的关键性部分。一旦你得到正确的结果,相应的实现很大程度上就是直截了当的。但是仍然有一些注意事项需要当心。过早地定义变量会对性能产生拖累。过度使用强制转换会导致缓慢的,难以维护的,被微妙的 bug 困扰的代码。返回一个类内部构件的句柄会破坏封装并将空悬句柄留给客户。疏忽了对异常产生的影响的考虑会导致资源的泄漏和数据结构的破坏。过分内联化(inlining)会导致代码膨胀。过度的耦合会导致令人无法接受的漫长的建构时间。 这一切问题都可以避免。

  只要有可能就推迟变量定义

  只要你定义了一个带有构造函数和析构函数的类型的变量,当控制流程到达变量定义的时候会使你担负构造成本,而当变量离开作用域的时候会使你担负析构成本。如果有无用变量造成这一成本,你就要尽你所能去避免它。

  你可能认为你从来不会定义无用的变量,但是也许你应该再想一想。考虑下面这个函数,只要 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 的时候没有任何初始化参数。这就意味着很多情况下将使用它的缺省构造函数,对于一个对象你首先应该做的就是给它一些值,这经常可以通过赋值来完成我已经解释了为什么缺省构造(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

  ·只要有可能就推迟变量定义。这样可以增加程序的清晰度并提高程序的性能。
下一篇:C++箴言:将强制转型减到最少
上一篇:用VC++实现http代理
IP查询、IP签名
打印 】【 收藏 】【 评论 】【 推荐
相关文章 栏目推荐文章 栏目热门文章
 推荐文章 C# 3.0语言详解之基本的语言增强 .. 
 推荐文章 C++程序设计最佳实践 
 推荐文章 C语言嵌入式系统编程之屏幕操作 
 推荐文章 QQ用户小心!C语言实现QQ密码大盗.. 
 推荐文章 C语言初学者入门讲座 第一讲 慨述.. 
 推荐文章 用C++实现简单的文件I/O操作 
 推荐文章 C++的iostream标准库介绍以及对左.. 
 推荐文章 用C++访问SQL Server 2000 
 普通文章 伪随机数生成及在VC++中的实现 
 推荐文章 TurboC程序设计初步  
 推荐文章 QQ用户小心!C语言实现QQ密码大盗.. 
 普通文章 C语言程序设计基础之联合 
 普通文章 学C++不得不看的一篇文章 
 推荐文章 强大的语言——C入门  
 推荐文章 C# 3.0语言详解之基本的语言增强 .. 
 推荐文章 用C++实现简单的文件I/O操作 
最新文章 最新推荐文章 热门文章
 普通文章 简历封面 
 普通文章 简历封皮(二) 
 推荐文章 用好Windows共享 确保安全 
 推荐文章 多点出发:消除局域网遗留共享痕迹.. 
 普通文章 只需三步!闪盘立刻变成启动盘 
 普通文章 战无不胜 一目了然看穿局域网 
 普通文章 Win 2000/XP在局域网内批量升级 
 普通文章 黑客经常更改的系统配置文件及注册.. 
 推荐文章 用好Windows共享 确保安全 
 推荐文章 多点出发:消除局域网遗留共享痕迹.. 
 推荐文章 Ghost二十个鲜为人知的实用参数 
 推荐文章 ASP.NET程序中常用的三十三种代码.. 
 推荐文章 黑客利用Ms05002溢出找“肉鸡” 
 推荐文章 C# 3.0语言详解之基本的语言增强 .. 
 推荐文章 Ghost所有运行错误代码完全大揭秘.. 
 推荐文章 利用Java Swing 实现游戏开发 
 推荐文章 秘籍:QQ头像任意换 
 普通文章 提升QQ等级 QQ挂机升级外挂  
 普通文章 QQ100个经典个性签名!可爱经典贴图.. 
 推荐文章 BIOS设置图解教程(多图) 
 普通文章 惊现腾讯QQ刷等级漏洞 
 普通文章 求职简历封皮  
 普通文章 突破在线观看电影 
 普通文章 GIF动画图片制作教程 
点击查看更多评论
笔名:
评论:
[评论将在5分钟内被审核,请耐心等待]
【注】 发表评论必需遵守以下条例:
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款
  • 本站大部分为网络转载,如有版权问题,请通知我们,我们立即更正!

设置首页 - 版权声明 - 广告服务 - 关于我们 - 联系我们 - 友情连接
Copyright ©2003-2005 xker.com All rights reserved. 网站合作、广告联系QQ:12231446
小新技术网  冀ICP备05002857号