您的位置:首页 > Web前端

条款21:必须返回对象时,别妄想返回其reference

2014-05-30 10:48 274 查看
条款21:必须返回对象时,别妄想返回其reference
引用只是对象的一种别名当使用引用的时候,请确认他的另一个身份在哪?

class Rational
{
public:
Rational(int x, int y) : m_x(x), m_y(y){}

//返回const是属于类型保护,friend修饰,以后条款详说
friend const Rational operator + (const Rational &lhs, const Rational &rhs)
{
Rational temp(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y); //还有更好的做法
return temp;
}
private:
int m_x;
int m_y;
};
实现+号重载,我们采用return value,根据前面条款,返回value会调用一系列拷贝构造函数,析构函数的成本,于是我们修改为return reference

friend const Rational& operator + (const Rational &lhs, const Rational &rhs)
{
Rational temp(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y); //还有更好的做法
return temp;
}

+号返回的引用对象在哪?我们看到在函数中创建一个临时对象temp,这个是属于栈空间对象,跳出函数作用域被回收,则引用的对象已经不存在了,程序会出现不可意料的行为。

上面我们提到函数实现还有一个更好的做法
friend const Rational operator + (const Rational &lhs, const Rational &rhs)
{
return Rational(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y); //标准做法
}
分析下:第一种做法是创建临时对象,调用构造函数初始化,返回时调用拷贝构造函数初始化存放返回值的外部变量,最后调用析构函数。而第二种是编译器直接创建临时对象并初始化外部变量,省去调用拷贝构造函数和析构函数。

如果考虑到在heap里面创建对象不会容易被回收,并使用reference返回,那么这个代价更大。

friend const Rational& operator + (const Rational &lhs, const Rational &rhs)
{
return *(new Rational(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y));
}
看下面调用
Rational x(1,2), y(3, 4), z(5, 6);
Rational r = x + y + z;
调用了两次operator +,两次new调用分配内存,但没有合理的方法delete内存,因为没有合理的方法取得返回reference的那个指针,导致内存泄露。

如果还是希望让函数返回reference而通过定义一个local-static对象替代heap对象,那么这个代价不比heap方法的小。

friend const Rational& operator + (const Rational &lhs, const Rational &rhs)
{
static Rational sRational(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y);
return sRational;
}

使用了静态变量在多线程安全性中存在隐患这一个问题暂且不说,还有一个是调用判断相等时出现逻辑错误

调用==判断

Rational x(1,2), y(3, 4), z(5, 6), w(7, 8);

if ((x + y) == (z + w)) {
cout<<"equal\n";
}
因为+号返回引用,而引用执行的对象时static,两次的+号返回reference其实是指向同一个对象,那==值判断永远相等。

记住
绝不要返回pointer或reference指向一个local stack对象,或返回reference指向一个heap-allocated对象,或返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象.条款4已经为"在单线程环境中合理返回reference指向一个local
static对象"提供一份设计实例.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐