13 More Effective C++—条款18/19(提前求值/临时对象的来源)

1 提前求值

1 概念

上一篇介绍了“延缓求值”——lazy evalute策略,其实质是:只有在真正需要数据的时候,才对计算进行求值。同时,常用的一种的策略是“马上求值”——eager evaluate,即只要出现计算表达式,就进行求值。

上面两种方案都没有考虑到,一次性大规模计算会让用户长时间等待。基于此,本篇提出“超急求值”——over eager evaluate。它实质是一种分摊(也可以说是分治)策略。

如下面代码,若otherFunc()会先于getMin()和getMax()调用,则m_max和m_min的计算可以放到otherFunc()中,而不必在每次需要min和max两个值时,都对数据集进行遍历,再求出min和max。

class ScoreList {
	public:
		void otherFunc();
		double getMin();
		double getMax();
	private:
		double m_max, m_min;
	}

这种方法通过将大规模计算分摊到其他操作中,并将计算结果永久性保存下来,降低了单次用户响应时间——注意,由于执行的语句数量不变,因此程序总运行响应时间不会有很大变化。

2 应用场景

下面提出如下几种应用场景。
1,cache(缓存)策略: 如果内存空间足够大,可以在进行其他操作的时候,顺带将磁盘中的数据库数据读取到内存中,每次访问某条数据,直接读取内存中的变量即可。

2,prefetch(预取出)策略:cache策略只取出并暂存需要的数据。由于2/8原则,可以当前需要数据临近区域的数据取出作进一步缓存,存储在内存中,从而提高数据访问速度。

3,std::vector的空间分配:每次vector的空间耗尽时,若继续向vector中存储数据,系统会做如下操作:

分配新空间 -> 复制原有数据到新空间 -> 将新数据插入到新空间。

新分配的空间在内存扩张时,将分配2倍原有空间,以防止后续插入操作,再次引起原有数据的复制操作。

3 总结

至此,关于求值策略,我们提出三种方法:马上求值,延缓求值,超前求值。三种方法无非就是“时间与空间的博弈”——用延长时间换空间减小,或者用空间减小换时间缩短。

2 临时对象的来源

临时对象并非下面代码所示,在函数中声明一个变量temp,temp只是函数中的局部对象。

void func() {
	int temp = 0;
	// do something
}

临时变量有两个来源:

1,隐式类型转换。
2,函数返回的对象。

1 隐式类型转换

如下面代码所示,隐式类型转换有多种形式。

其中,情形2的转换,只会发生在"按值传参"和“const 引用传参”的情况下, 这两种允许兼容的不同类型对象进行相互转换。“引用传参”和“指针传参”会严格按照类型进行匹配,不会出现类型转换。

// 情形1:1.23临时转换,得到一个临时int类型对象,然后赋值给a
int a = static_cast<int>(1.23); 

// 情形2:实参与形参类型不一致,实参转换类型,生成string临时对象
int countChar(const std::string& str);
char *str = "hello world";
countChar(str); // 函数返回,临时对象被销毁

int countChar(std::string &str);
countChar(str); // 错误,char*和std::string&类型不兼容

2 返回值创建临时对象

这种情况指每次函数返回都会产生新的临时对象。如下面代码所示的集中情形。

// 情形1:返回变量greet引起临时变量创建,这个变量没有名称——匿名变量。
std::string createStr() {
	std::string greet;
	return greet;
}

// 情形2:调用operator + 操作符。a+c会产生新的临时变量,并赋值给a
// 优化方法:使用+=操作符,a += c
MyClass a = a + c

// 情形3:调用“前置自增”操作符,a++
int a = 0;
a++;

针对情形1,需要特别注意不能返回函数内局部变量的引用。如下面代码。

const std::string &createStr() {
	return std::string();
}

// 或者
std::string &createStr() {
	return std::string();
}

对于情形3, 需要注意前置自增实际上进行两个操作,先获得原始值,然后再进行自增运算。这样会产生原始值的临时变量。因此,若只是想做自增运算,要使用“后置自增”。

int a = 0;
++a; // 只是自增运算
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值