Effective C++ 学习笔记 《二十六》

Item 26: Postpone variable definitions as long as possible.

这一节作者谈到的内容是关于变量的定义时机问题。需要考虑这样的一个问题的原因就在于当定义一个变量的时候,就意味着对这个变量对象进行构造(如果有构造函数),以及在离开它作用域的时候进行析构,也就是说需要承担这些消耗。那么如何避免没有意义的消耗就是一个值得考虑的话题
作者给出的建议就是尽可能的推迟变量的定义 来看例子

// 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;
}

上面的程序中encryptPassword是对传入的参数password进行加密,在函数中声明了一个作为返回的加密的string对象encrypted。
这种代码组织方式看上去没什么问题,但设想如果在后面的if语句中抛出了异常,这就意味我们前面定义的string encrypted没被用上,也就意味着我们白白付出了对string的构造以及后面的析构代价。

改进的方法,自然是不急着定义这个字符串,等到真正需要使用的时候才定义,从而减轻一定的损失可能性。就像下面的代码

// 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的定义放到异常抛出之后,也就是真正需要用的时候,因为下面就是对它进行处理。


看到这似乎已经把这一节的内容讲完了,但作者在后面继续对上面的代码进行了一些优化,其实也是对之前的内容的回顾。
上面的代码中,我们是定义了一个string encrypted,它将执行默认初始化,接着我们肯定需要对它处理,而且需要用到实参password,所以我们做的操作是下面这样的代码

// 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;
}

它先默认初始化,再赋值,这其实意味着默认初始化白费了,更好的写法就是直接进行拷贝构造函数来省去无用功

// 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;
}

通过这个例子作者是想告诉我们,有时候不仅是要尽可能的推迟变量定义到不得不用为止,更有可能是需要推迟到必须初始化从而避免默认初始化的无用消耗。

接着作者谈到了再循环中的情况,举出两个例子对比

// 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 );
	   ...														...
} 															}

方法A中,将w放在循环外,需要一次默认初始化,最终也需要一次析构 然后循环中执行n次赋值 一共是一次构造,一次析构,n次赋值
方法B中,将w放在循环内,每次循环需要一次拷贝构造,但是由于是局部变量,每次循环也会产生一次析构,所以是n次构造,n次析构

如何比较两者性能?我们看这两个式子,起关键作用的是n后面的动作,也就是比较n次赋值和n次构造加析构的代价。
如果一次赋值的代价小于一次构造和一次析构,那方法A肯定的是高效 否则方法B更好,而且方法A的问题在于w的作用域更广,不利于理解和维护。所以综上给出的建议就是:

1. 知道一次赋值的代价小于一次构造和析构,而且这段代码对性能要求很高,我们才选择A方法
2. 其他情况选择B方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值