1

考虑下面的代码:

#include <iostream>
#include <stdexcept>

using namespace std;

int i;

class A{
    public:
    ~A(){i=10;}
    };

int func1()
{
    i=3;
    A Ob; // -> here local object , hence created and destroyed
    return i;
    }

int& func2()
{
    i=8;
    A obj;
    return i;
    }

int func3()
{
    i=8;
    {A  obj;}
    return i;
    }





int main()
{

cout << "i : " <<func1() << endl;
cout << "i : " <<func2() << endl;
cout << "i : " <<func3() << endl;
return(0);
}

输出:

$ ./TestCPP
i : 3
i : 10
i : 10

有人可以解释为什么首先 i 是 3 吗?In func1(),A Ob是局部变量,因此它被创建和销毁。当它被销毁时,它将调用其析构函数将 i 修改为10并且我期望i10,但答案显示i : 3

4

5 回答 5

4

调用时堆栈对象仍在作用域内return,因此值i仍然为 3,因为A尚未调用析构函数 for。当函数的堆栈展开时,堆栈对象将被删除,这是设置返回值之后。

想象一下,如果不是这种情况,堆栈对象是否可以在return. 你怎么能从函数中返回一个本地值?

对评论的回应

@paddy 在那种情况下你能解释一下 func2 和 func3 吗?

从表面上看,func2看起来与 几乎完全相同func1,您会认为它应该返回 8 是可以原谅的。但这里的不同之处在于它返回int&而不是int。这意味着对的引用ireturn i;. 即使i堆栈开始展开时为 8,但到时间obj被销毁并将返回值弹回给调用者时,值为i10。因为我们返回了一个引用,所以返回值被取消引用,并且当前值i(10 ) 用来。

因为func3它更容易。这返回一个正常的int,就像func1. 但是该实例A obj;在其自己的块范围内:{ A obj; }. 所以它在 之前被销毁,当我们从函数返回时return,它的值为10。i

于 2013-06-28T01:58:22.883 回答
2

来自标准(C++11、3.7.3):

(1) 显式声明的块范围变量 register 或未显式声明的 static 或 extern 具有自动存储持续时间。这些实体的存储一直持续到创建它们的块退出。

[...]

(3)如果一个具有自动存储持续时间的变量有初始化或者有副作用的析构函数,在它的块结束前不能被销毁,即使它看起来没有被使用也不能作为优化被淘汰,除非一个可以按照 12.8 中的规定消除类对象或其复制/移动。

这意味着A无论声明它的块在哪里结束,它的生命周期都会结束。在 的情况下func1,这是在返回语句之后。在这种情况下func3是在返回语句之前。

(“块”是用花括号括起来的一段代码:{...}.)

因此,在调用func1的析构函数之前评估返回值并因此返回。在调用析构函数后计算返回值并因此返回。A3func310

在 的情况下func2,顺序与 中相同func1,但因为它返回一个引用,所以由 的析构函数执行的值修改A,即使它是在对返回值求值之后执行的,也会对原来的值产生影响。回来。

于 2013-06-28T02:05:17.647 回答
2

它与在调用 A 析构函数之前是返回 i 的副本还是对它的引用有关:

func1() 案例:

  1. 我设置为 3
  2. i 的值作为 i 的副本返回(作为临时的)
  3. i 在 A 析构函数中设置为 10
  4. i (3) 的副本已打印

func2() 案例:

  1. 我设置为 8
  2. i 的值作为对全局变量 i 的引用返回
  3. i 在 A 析构函数中设置为 10
  4. 打印 i 的当前值

func3() 案例:

  1. 我设置为 8
  2. i 在 A 析构函数中设置为 10
  3. i 的值作为副本返回
  4. 打印 i (10) 的副本
于 2013-06-28T02:08:06.060 回答
0

析构函数出现在 return 语句之后,调用函数的下一行之前。返回值保存在隐藏变量中,然后调用析构函数和其他函数清理(如返回堆栈),然后在调用者中继续执行。

为了进一步澄清 - 想象返回线是return i+A.foo();。在此行之后,您不希望调用析构函数,否则 A 调用 foo 无效。这就是为什么在返回后总是调用析构函数的原因。

于 2013-06-28T01:57:49.067 回答
0

变量“i”在整个代码中都是全局的,没有它的“本地”实例。

对象在超出范围时被销毁,这是您的函数中的“返回”之后,而不是在它之前。所以第一个 dtor 仅在 i 的第一个值返回并且函数结束后才被调用。

   i = 3; <-- sets i to 3
   A Ob; <-- constructs object.
   return i; <-- places the value "3" in the return value register.
} <-- destroys object changing future values of "i" to 10.

如果您想返回“i”而不是它在表达式“return i”时包含的值,则必须进行以下更改:

int& func1()
{
   i = 3; <-- sets i to 3
   A Ob; <-- constructs object.
   return i; <-- places a reference to "i" in the return value register.
} <-- destroys object changing the value of "i" to 10

http://ideone.com/XXYu2u

我强烈建议您在使用调试器之前和之后通过该程序来更好地熟悉整个过程 - 这是巩固您对正在发生的事情的理解的最佳方式。

于 2013-06-28T02:24:47.283 回答