我知道使用引用返回临时变量不起作用,因为临时对象在函数终止后会丢失,但是由于返回的临时对象被分配给另一个对象,所以下面的代码可以正常工作。
我假设在函数调用行之后临时对象被销毁。如果是这样的话,为什么这种方法不能用于链接呢?
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;
}
答案 0 :(得分:3)
如果函数返回对本地对象的引用,则该函数返回后将立即销毁该对象(如本地对象所示)。它不持续到函数调用行的末尾。
在销毁对象后访问该对象将产生不可预测的结果。有时它可能有用,对于某些“工作”的定义,有时可能没有。只是不要这样做。
答案 1 :(得分:0)
Counter& doubler()
{
Counter tmp;
tmp.i=this->i*2;
return tmp;
}
这是未定义的行为。从函数返回后 - 您的引用将悬空,因为将为本地对象Counter
调用tmp
析构函数。
答案 2 :(得分:0)
您正在返回对本地(临时)对象的引用。退出函数后,它是未定义的行为。
因为tmp
将在返回函数后被销毁。因此,返回的引用无效。
答案 3 :(得分:0)
真正的问题不是“为什么这种方法链不起作用?”而是“为什么第一个('正常')函数调用有效?”
答案是没有办法说出来,因为它可能会破坏你的程序。
要清楚说明:通过引用返回临时对象是未定义的行为。当然,这意味着它今天可能会巧合而且明天就会停止工作。所有赌注都已关闭。
答案 4 :(得分:0)
当函数返回并发生堆栈回滚时,逻辑回滚堆栈指针设置为不同的值。如果函数返回局部变量引用,则指向本地的内存位置可能仍处于进程中并且设置了相同的位。但是,这不能得到保证,并且在几次调用之后无效并且可能导致未定义的行为。
答案 5 :(得分:0)
其他都可以,因为“只是不要乱用本地对象的引用”
但至于为什么它适用于一个案例而不是其他案件
当你单独调用它时,当函数返回时,对象仍然躺在堆栈上。授予一个“被破坏的”对象 - 但是对象过去采取的任何空间仍然存在于堆栈中。如果你有一个简单的对象,比如一个int成员,那么堆栈上就会有令人不安的东西,除非你在堆栈上编写了其他东西的代码,或者析构函数决定做一个更彻底的工作并删除一个整数成员(大多数析构者都不这样做。雅达亚达,但直到下一行不会发生太大的事情,将其从堆栈中移出。你的引用指向一个有效的内存位置,你的(被破坏的)对象就在那里。这就是为什么它适合你。
当您将其称为链接时,请参阅第一个调用,返回对堆栈上该tmp的引用。正如上面#1中所解释的那样,到目前为止没问题。你的(破坏的)tmp仍然存在于堆栈中。但请注意你称之为第二个倍增器的那一刻。第二个双倍函数调用中的tmp会出现在哪里?就在你第一次通话的tmp !!!第二个调用使用值为0的tmp(默认构造的一个)覆盖对象(值为4的tmp)。第二个调用实际上是在一个具有0值的计数器上进行的,因此你得到0.非常棘手 - 这就是为什么忘记摆弄返回对局部变量的引用。
现在纯粹主义者可能尖叫 - 未定义,不,不,只是不这样做 - 我和他们在一起 - 我自己说了两次(现在三次)不这样做。但人们可能会尝试。我打赌像下面这样的'简单'对象,并且代码与问题完全相同(因此没有任何东西扰乱堆栈),每个人都会得到一致的4,0 - 没有随机性,没有未定义.... / p>
class Counter
{
public:
Counter()
{
i = 0;
}
Counter(int k)
{
i = k;
}
int get()
{
return i;
}
int i;
Counter& doubler();
};