• 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)

  • 相关阅读:
    HDU 4539郑厂长系列故事――排兵布阵(状压DP)
    HDU 2196Computer(树形DP)
    HDU 4284Travel(状压DP)
    HDU 1520Anniversary party(树型DP)
    HDU 3920Clear All of Them I(状压DP)
    HDU 3853LOOPS(简单概率DP)
    UVA 11983 Weird Advertisement(线段树求矩形并的面积)
    POJ 2886Who Gets the Most Candies?(线段树)
    POJ 2828Buy Tickets
    HDU 1394Minimum Inversion Number(线段树)
  • 原文地址:https://www.cnblogs.com/lidan/p/2325108.html
Copyright © 2020-2023  润新知