考虑以下代码
int i;
class A
{
public:
~A()
{
i=10;
}
};
int foo()
{
i=3;
A ob;
return i;
}
int main()
{
cout << foo() << endl;
return 0;
}
由于i
是全局的,我认为该程序的输出应为10
。 ob
,当它超出范围时,将调用析构函数,该析构函数应将i
的值设置为10
。
答案 0 :(得分:16)
局部变量超出范围,并且在函数返回后执行它们的析构函数。这意味着,在~A()
运行时,返回值已经为3
。相反,如果你这样做......
int foo()
{
{
i=3;
A ob;
}
return i;
}
... ob
将超出范围并在return语句之前死亡,结果将为10。
对于像scoped_lock
这样的RAII活动类型,通常以这种方式使用额外的范围,以仅针对函数的一部分进入某种特定的副作用状态。
答案 1 :(得分:7)
int foo()
{
i=3;
A ob;
return i;
} // <- ~A() is called here
当~A()
超出范围时,在函数foo()
的末尾调用 ob
。这是在计算返回值之后(即i
的副本,当时的值为3
。
为了在函数退出之前将i
设置为10
,您需要强制ob
更早超出范围&#34;。最简单的是在A ob
附近添加一个额外的范围。
int foo()
{
i=3;
{
A ob;
} // <- ~A() is called here and sets i to 10
return i;
}
答案 2 :(得分:4)
ob
超出范围。考虑如果ob
参与评估返回表达式会发生什么,并且在返回之前它会超出范围。
答案 3 :(得分:4)
ob
在范围结束之前不会超出范围,即在}
语句之后发生的return
。这就是析构函数被调用的时候,但到那时i
已被评估,所以它的旧值被返回。
答案 4 :(得分:3)
~A()
超出范围时调用ob
。
装配证明:
<__Z3foov>:
push %ebp
mov %esp,%ebp
push %ebx
sub $0x10,%esp
movl $0x3,0x407020 /* <-- setting i to 3 */
mov 0x407020,%ebx /* <-- loading i to %ebx */
lea -0x5(%ebp),%eax
mov %eax,%ecx
call 403860 <__ZN1AD1Ev> /* <-- calling destructor */
mov %ebx,%eax /* <-- returnign already calculated i from %ebx */
add $0x10,%esp
pop %ebx
pop %ebp
ret
<__ZN1AD1Ev>: /* <-- the destructor */
push %ebp
mov %esp,%ebp
sub $0x4,%esp
mov %ecx,-0x4(%ebp)
movl $0xa,0x407020 /* <-- set i to 10 */
leave
ret
正如您所看到的,在将返回值放入%ebx
之后调用析构函数,当析构函数完成时,它再次移动到%eax
。对于您将来有关C ++代码行为的问题,反汇编是您最好的朋友。