c++中究竟什么时候调用析构函数?
When exactly is the destructor called in C++
考虑以下代码
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
局部变量超出作用域,并执行其析构函数,在函数返回后。这意味着当~A()
运行时,返回值已经是3
。相反,如果你这样做……
int foo()
{
{
i=3;
A ob;
}
return i;
}
…ob
将超出作用域并在返回语句之前死亡,结果将是10。
额外作用域通常以这种方式用于raii活动类型,如scoped_lock
,以便仅对函数的一部分进入某些特定的副作用状态。
int foo()
{
i=3;
A ob;
return i;
} // <- ~A() is called here
当ob
超出作用域时,在函数foo()
的末尾调用 ~A()
。这是在计算返回值(即i
的副本,当时的3
的值)之后。
为了在函数退出之前将i
设置为10
,您需要强制ob
"提前退出作用域"。最简单的方法是在A ob
周围添加一个额外的作用域。
int foo()
{
i=3;
{
A ob;
} // <- ~A() is called here and sets i to 10
return i;
}
ob
在返回执行后超出作用域。考虑一下,如果ob
参与了返回表达式的求值,并且它在返回之前超出了作用域,将会发生什么。
ob
直到作用域结束,即在return
语句之后的}
处才会超出作用域。这时析构函数被调用,但此时i
已经被求值了,所以它的旧值被返回。
当ob
超出作用域时调用~A()
。大会证明:
<__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++代码行为的问题,反汇编是您最好的朋友。
相关文章:
- 析构函数调用
- 在具有向量的类构造函数中进行析构函数调用
- 从 c++ 中派生类的析构函数调用虚函数
- C++析构函数调用两次,堆栈分配的复合对象
- C++ 在析构函数调用之前删除的动态成员数组
- 析构函数调用c++中的一个向量
- Singleton模式中的手动析构函数调用:调用多次
- 从内部类的析构函数调用虚拟函数
- 与 boost odeint 集成期间的析构函数调用
- 堆栈展开如何与析构函数调用有关?
- C++:优化析构函数调用
- 以逗号分隔的表达式中的析构函数调用
- GCC 9.1 返回 void& 作为显式析构函数调用的结果类型。这是一个错误吗?
- 从C++中的虚拟析构函数调用虚拟方法
- 从指针返回对象时出现意外的析构函数调用
- 使用 decltype 显式析构函数调用
- C++析构函数调用了错误的对象
- 了解虚拟函数和析构函数调用
- 多重继承析构函数调用他自己和父析构函数?c++
- 析构函数调用表单不适当的库