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

考虑有理数的class:

class Rational
{
public:
    Rational(int numerator = 0, int denominator = 1);
protected:
private:
    int n, d;
    friend Rational operator*(const Rational& lhs, const Rational& rhs);
};

by value版的operator*, 你可能想改而返回reference,以根除pass-by-value的种种邪恶,省掉构造和析构,但是,

所谓reference只是个名称,代表某个既有对象,任何时候看到一个reference声明式,你都应该立刻问自己,它的另一个名称是什么

如果operator*要返回一个reference指向如此数值,他必须自己创建那个Rational对象。

const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
    Rational result(lhs.n * rhs.n, lhs.d * rhs.d); //警告!糟糕的代码!
    return result;
}

你的目标是要避免调用构造函数,而result却必须像任何对象一样地由构造函数构造起来。更严重的是result是一个local对象,而local对象在函数退出前被销毁了。因此这个返回的reference指向的是一个“从前的”Rational;如今已经被销毁了。任何调用者甚至对此函数的返回值做任何一点点运用,都会陷入“无定义行为”的恶地。

于是考虑在heap内构造一个对象, 并返回reference指向它。

const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
    Rational* result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d); //更糟的代码!
    return *result;
}

你还是必须付出一个构造函数调用的代价,此外又有另一个问题:谁该对着被你new出来的对象实施delete?

Rational w, x, y, z;

w = x * y * z; //与operator*(operator*(x, y), z)相同

同一个语句调用了两次operator*, 因而两次使用new,也就需要两次delete。但却没有合理的办法让operator*使用者进行delete调用, 因为没有合理的办法让他们取到operator*返回的reference背后隐藏的那个指针。这绝对导致资源泄漏。

或许你想到一种办法可以避免任何构造函数被调用。“让operator*返回reference指向一个被定义于函数内部的static Rational对象

const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
    static Rational result; //又是一堆烂代码。
    result = ...; //lhs*rhs
    return result;
}

就像所有用上static对象的设计一样, 这个也立刻造成我们对多线程安全性的疑虑。只是显而易见的弱点。更深层的瑕疵:

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

表达式if ((a*b) == (c*d))总是被核算为true, 不论a,b, c,d是什么。

在operator==被调用前,已有两个operator*调用式起作用,每个都返回operator*内部定义的static Rational对象的reference,这个将“operator*内的static Rational对象值”和“operator*内的static Rational对象值”比较,不相等才怪。(虽然两次调用operator*都改变了static Rational对象的值, 但是返回的reference, 调用端看到的永远是static Rational的现值)。

一个“必须返回新对象”的函数的正确写法:就让那个函数返回一个新对象。

inline const Rational operator*(const Rationa& 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对象而有可能同时需要多个这样的对象。条款4已经为“在单线程环境中合理返回reference指向一个local static对象”提供了一份设计实例(singleton)

原文链接: https://www.cnblogs.com/lidan/archive/2012/01/17/2325108.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

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

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/40358

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月8日 下午5:02
下一篇 2023年2月8日 下午5:03

相关推荐