条款21:必须返回对象时,别妄想返回其reference
(Don't try to return a reference when you must return an object.)
内容:
前面一款我们提到了pass-by-value方式参数传递的效率带来的一系列可能引发的问题,你可能对pass-by-
reference-const的参数传递方式特别的'青睐',而现在我们需要在在一次函数调用中要返回一个对象,问题是我
们将以何种方式返回这个对象,你这个时候迫不及待的告诉我你的方案:直接返回该对象的reference.嗯,我们先
用你的方案去解决下面这个问题,假设现在我们需要处理有理数Rational类:
class Rational{
public:
Rational(int numerator=0,int denominator=1);
...
private:
int numerator_,denominator_;
friend Rational& operator* (const Rational& lhs,const Rational& rhs);
};
...
Rational& operator*(const Rational& lhs,const Rational& rhs){
Rational result( lhs.numerator_ * rhs.numerator_, lhs.denominator_ * rhs.denominator_ );
return result;
}
哦吼,问题来了,你返回的对象result是一个local对象,此对象在函数退出时候自动调用析构函数来销毁自己,
不存在了,你现在却在返回一个不存在对象的引用,你就会陷入"未定义"的危地.你这时立刻就想到了一个解决方案:
在heap上产生该对象不就问题解决了嘛,因为堆上的对象不会自动销毁自己(除非你手工调用delete操作).ok,那么
我们再来改一下代码看这种方案是否可行:
Rational& operator*(const Rational& lhs,const Rational& rhs){
Rational* result=new Rational(lhs.numerator_ * rhs.numerator_,
rhs.denominator_ * rhs.denominator_);
return *result;
}
这样实现貌似很合理,其实这里还是存在一个问题:你new出来的对象何时被delete掉,谁负责执行delete操作.
如果客户在使用这个类的时候写出如下代码:
Rational x,y,z,w;
w=x*y*z; //operator*(x,operator*(y,z));
这个时候operator*(y,z)对象的reference(其实就是指针)就会丢失,你就无法执行delete操作,那么这里明显就
存在了内存泄露的风险.晕,这样也不行,这时聪明的你灵感又来了:用static修饰local对象.你这样想的理由可能是下
面两点:(1)static对象常驻内存,一旦对象被创建,你就不能随意去销毁它,除非程序结束;(2)每次调用该函数只是修
改该对象的值,该对象始终只会存在一个.听起来想法很不错,我们依然还是先修改代码,看这方案是否可行?
Rational& operator*(const Rational& lhs,const Rational& rhs){
static Rational result;
...
result=...;
return result;
}
而现在我们的客户却一不小心写下了这样的代码:
Rational a(2,3);
Rational b(4,5);
Rational c(4,7);
Rational d(7,8);
if( (a*b) == (c*d)){
...
}
晕,问题又来了:a*b与c*d返回的对象引用都是引用的同一个static对象,那么这样的比较就显的没有意义了,因为
结果一直都是true,哎呀,方案又'破产'了,问题没解决,心情很郁闷?别急,其实这里的解决方案很简单:我们只需要直接
返回该对象就行了,我们先看一下最新的代码:
const Rational operator*(const Rational& lhs,const Rational& rhs){
return result(lhs.numerator_ * rhs.numerator_,lhs.denominator_ * rhs.denominator_);
}
呵呵,现在你看看所有问题不是都解决了嘛!当然,你需要承受operator*返回值的构造成本和析构成本,然而长远来
看那只是为了获得正确行为而付出的一个小小代价,好了,该条款至此就over了!
请记住:
◆ 绝不要返回pointer或reference指向一个local stack对象,或返回reference指向一个heap-allocated对象,或
返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象.条款4已经为"在单线程环境中合理
返回reference指向一个local static对象"提供一份设计实例.

343

被折叠的 条评论
为什么被折叠?



