0

我知道使用引用返回临时变量不起作用,因为临时对象在函数终止后丢失,但以下代码有效,因为返回的临时对象被分配给另一个对象。

我假设临时对象在函数调用行之后被销毁。如果是这样,为什么这不适用于这种方法链接?

Counter& Counter::doubler()
{ 
   Counter tmp;
   tmp.i = this->i * 2;
   return tmp;
}

int main()
{
    Counter d(2);
    Counter d1, d2;
    d1 = d.doubler();                            // normal function call
    std::cout << "d1=" << d1.get() << std::endl; // Output : d1=4 
    d2 = d.doubler().doubler();                  // Method chaining
    std::cout << "d2=" << d2.get() << std::endl; // Output : d2=0 
    return 0;
}
4

5 回答 5

3

如果函数返回对本地对象的引用,则该对象将在函数返回后立即销毁(与本地对象一样)。它不会持续到函数调用的行尾。

在对象被销毁后访问它会产生不可预知的结果。对于“工作”的某些定义,有时它可能会起作用,有时它可能不会。只是不要这样做。

于 2013-04-12T09:55:42.690 回答
0
Counter& doubler()
{ 
Counter tmp;
tmp.i=this->i*2;
return tmp;
}

这是未定义的行为。从函数返回后-您的引用将悬空,因为Counter将为 local object 调用析构函数tmp

于 2013-04-12T09:55:33.393 回答
0

真正的问题不是“为什么这种方法链接不起作用?”,而是“为什么第一个('正常')函数调用有效?”

答案是没办法说,因为它还不如破坏你的程序。

明确地说:通过引用返回临时对象是未定义的行为。当然,这意味着它今天可能会巧合,明天就会停止工作。所有的赌注都取消了。

于 2013-04-12T10:00:45.890 回答
0

其他都很好,因为“只是不要摆弄对本地对象的引用”

但至于为什么它在一种情况下有效,而在另一种情况下无效

  1. 当你单独调用它时,当函数返回时,对象仍然躺在堆栈上。授予一个“已破坏”的对象 - 但该对象过去占用的空间仍然存在于堆栈中。如果你有一个简单的对象,比如只有一个 int 成员,那么堆栈上不会有任何干扰,除非你在堆栈上分配了其他东西,或者析构函数决定做一个彻底的工作并删除一个整数成员(大多数析构函数不这样做)。授予 yada yada,但直到下一行不会发生太多将其从堆栈中移出的情况。您的引用指向一个有效的内存位置,并且您的(被破坏的)对象将在那里。这就是为什么它对你有用。

  2. 当您将其称为链式调用时,请参阅第一个调用会返回对堆栈上该 tmp 的引用。正如上面#1 中所解释的,到目前为止没有问题。您的(破坏的) tmp 仍然在堆栈中。但是请注意您调用第二个加倍器的那一刻。第二个 doubler 函数调用中的 tmp 将出现在哪里?就在您第一次通话的 tmp 所在的位置!!!第二次调用用值为 0 的 tmp(默认构造的)覆盖对象(值为 4 的 tmp)。第二次调用实际上是在具有 0 值的 Counter 上进行的,因此您得到 0。非常棘手 - 这就是为什么忘记摆弄返回对局部变量的引用的原因。

现在纯粹主义者可能会尖叫 - 未定义,不,不,只是不这样做 - 我和他们在一起 - 我自己说过两次(现在三次)不这样做。但人们可以尝试一下。我打赌像下面这样的“简单”对象,并且代码与问题中的完全相同(以免干扰堆栈),每个人都会得到一致的 4, 0 - 没有随机性,没有未定义......

class Counter
{
public:
    Counter()
    {
        i = 0;
    }
    Counter(int k)
    {
        i = k;
    }
    int get()
    {
        return i;
    }
    int i;
    Counter& doubler();
};
于 2013-04-12T10:21:41.057 回答
0

当函数返回并发生堆栈回滚时,它是逻辑回滚,堆栈指针设置为不同的值。如果一个函数返回一个局部变量引用,那么指向本地的内存位置可能仍然在进程中并且具有相同的位设置。但是,这并不能保证,并且在多次调用之后将无效并且可能导致未定义的行为。

于 2013-04-12T10:04:00.060 回答