欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Effective C++ 详解条款21:必须返回对象时,别返回其reference

程序员文章站 2022-07-12 17:51:04
...

如果在stack空间创建一个local变量,并且你函数返回一个reference指向该local对象,将导致”无定义行为“,同样在,heap上也会导致operator*使用者不能获取到reference背后的指针而资源泄漏。

//考虑一个用以表现有理数(rational numbers)的 class,内含一个函数用来计算两个有理数的乘积:
class Rational {
public:
	Rational (int numerator = 0,int denominator  1) ;                      //条款24说明为什么这个构造函数不声明为explicit
	...
private:
	int n, d;                                                              //分子(numerator)和分母(denominator)
friend
	const Rational operator*(const Rational& lhs, const Rational& rhs) ;   //条款3说明为什么返回类型是const
};

//第一种情况 在stack空间创建新对象,定义一个local变量,就是在stack空间创建对象。
const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
   Rational result (lhs.n * rhs.n,lhs.d * rhs.d);   //警告!糟糕的代码!
   return result; 
}

//第二种情况 在heap内构造一个对象,并返回reference指向它。Heap-based对象由new创建,所以得写一个heap-based operator* 
const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
   Rational* result = new Rational(lhs.n * rhs.n,lhs.d * rhs.d);   //警告!糟糕的代码!
   return *result; 
}

第一种情况:可以拒绝这种做法,因为目标是要避免调用构造函数,而result却必须像任何对象一样地由构造函数构造起来。更严重的是:这个函数返回一个reference 指向result,但result是个local对象,而local对象在函数退出前被销毁了。因此,这个版本的operator*并未返回reference 指向某个Rational,它返回的reference 指向一个“从前的”Rational;一个旧时的Rational;-一个曾经被当做Rational但如今已经成空、发臭、败坏的残骸,因为它已经被销毁了。任何调用者甚至只是对此函数的返回值做任何一点点运用,都将立刻坠入“无定义行为”的恶地。

任何函数如果返回一个reference指向某个local对象,都是行不通的。同样,如果函数返回指针指向一个local对象,也是如此。

第二种情况:函数需要付出“构造函数调用”的代价,因为分配所得的内存将以一个适当的构造函数完全初始化动作。

不论是在stack空间内,还是heap空间,都因为对operator*返回的结果调用构造函数而受惩罚。

**一个“必须返回新对象”的函数的正确写法是:**就让那个函数返回一个新对象。对Rationaloperator*而言意味以下写法(或其他本质上等价的代码):

inline const Rational operator * (const Rational& lhs, const Rational& rhs)
{
    return Rational (lhs.n * rhs.n, lhs.d * rhs.d); 
}

总结:

绝不要返回pointer或reference指向一个local stack对象,或返回reference指向一个heap-allocated对象,或返回pointer或reference指向一个local static对象而有可能需要多个这样的对象。