Effective C++条款21:必须返回对象时,别妄想返回其引用

之前条款20说到,pass-by-reference比pass-by-value通常高效的多.
可能这会导致你,什么时候都用按引用来替代传值.
但是注意:所谓的引用只是给一个对象取了另一个别名, 当你见到引用时,一定要问一下自己,它另外一个名字是什么?因为引用一定是一个对象的另一个名称。

考虑有以下的类:

class Time
{
private:
	int hours;
	int minutes;
public:
public:
	Time();
	Time(int h, int m = 0);
	friend Time operator+(const Time & t, const Time & t1);
	friend Time operator-(const Time & t, const Time & t1);
	friend Time operator*(const Time & t, double n);
};

这个版本以by value方式返回一个计算结果,想想如果我们可以改成传递reference,就不需要付出代价,比如构造和析构成本。
如果operator*要返回一个reference,它必须自己创建那个Time对象,前面说过了所谓的引用只是给一个对象取了另一个别名。
按照这个规则,我们尝试这样写:

const Time& operator*(const Time & t, double n)
{

	long totalminutes = t.hours * n * 60 + t.minutes * n;
	Time result(totalminutes / 60,totalminutes % 60);
	return result;
}

这样的写法造成的问题是:result是一个局部对象,局部对象在函数退出前被销毁了,因此这个版本返回了一个无用的引用,它指向的对象已经被销毁了.调用这个引用,将会导致不可预料的错误.

或许我们可以考虑在堆内构造一个函数,可以这样写:

const Time& operator*(const Time & t, double n)
{

	long totalminutes = t.hours * n * 60 + t.minutes * n;
	Time result = new Time(totalminutes / 60,totalminutes % 60);
	return result;
}

但是这样还是付出一个构造函数调用代价,而且还有一个问题:谁该对被new出来的对象实施delete?
即使你delete了,考虑以下代码:

Time W,x,y,z;
W = x * y * z;

这里同一个语句调用了两次operator*,使用了两次new,也就需要两次delete.但是我们没有合理的办法让operator*使用者进行那些delete调用,因为没有合理的办法取得operator*返回的引用背后隐藏的那个指针

或许我们还可以通过静态对象来,考虑以下代码:

const Time& operator*(const Time & t, double n)
{
	static Time result;
    result = ...;
	return result;
}

问题在于:这可能会造成多线程安全性问题,此外考虑以下代码:

bool operator==(const Time& lhs, const Time&rhs);
Time a,b,c,d;
...
if((a*b)==(c*d))
{
	...
}
else
{
	...
}

这将导致(a*b)==(c*d)永远为true,无论a,b,c,d的值是什么.
将代码重写为等价的函数形式:

if(operator==(operator*(a,b), operator*(c,d)))

operator==被调用前,两个operator*已经起了作用。
每一个都返回引用指向operator*内部定义的静态Time对象。因此operator==被要求将"operator内的static Time对象值"拿来和"operator内的static Time对象值比较",所有比较结果相等(两次operator*调用的确各自改变了静态Time对象值,但是a b c d都引用了同一个静态对象,因此都相等.).

所以一个最好的写法就是,就让那个函数返回一个新对象:

Time operator*(const Time & t, double n)
{
	Time result;
	long totalminutes = t.hours * n * 60 + t.minutes * n;
	result.hours = totalminutes / 60;
	result.minutes = totalminutes % 60;
	return result;
}

总结:

不要返回指针或引用指向一个局部栈对象,或返回引用指向一个堆分配的对象,或返回指针或引用指向一个静态对象而有可能同时需要多个这样的对象。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值