返回信息流又来麻烦贵邮大神了,看程序时,意外看到编译器为我生成的代码,没有看明白。请指教
程序源码如下:
#include <iostream>
using namespace std;
class A {
public:
A() { cout << "A" << endl; }
~A() { cout << "~A" << endl; }
void operator delete(void *ptr) { cout << "AA" << endl; ::operator delete(ptr);};
private:
//void operator delete(void *ptr) { cout << "AA" << endl; ::operator delete(ptr);};
};
class B : public A {
public:
B() { cout << "B" << endl; }
~B() { cout << "~B" << endl; }
//void operator delete(void *){ cout << "BB" << endl; };
};
int main(void)
{
A *p = new B;
delete p;
return 0;
}
其生成的部分汇编如下:
000000000040099c <main>:
40099c: 55 push %rbp
40099d: 48 89 e5 mov %rsp,%rbp
4009a0: 41 54 push %r12
4009a2: 53 push %rbx
4009a3: 48 83 ec 10 sub $0x10,%rsp
4009a7: bf 01 00 00 00 mov $0x1,%edi
4009ac: e8 bf fe ff ff callq 400870 <operator new(unsigned long)@plt>
4009b1: 48 89 c3 mov %rax,%rbx
4009b4: 48 89 df mov %rbx,%rdi
4009b7: e8 20 01 00 00 callq 400adc <B::B()>
4009bc: 48 89 5d e8 mov %rbx,-0x18(%rbp)
4009c0: 48 8b 5d e8 mov -0x18(%rbp),%rbx
4009c4: 48 85 db test %rbx,%rbx
4009c7: 74 10 je 4009d9 <main+0x3d>
4009c9: 48 89 df mov %rbx,%rdi
4009cc: e8 ab 00 00 00 callq 400a7c <A::~A()>
4009d1: 48 89 df mov %rbx,%rdi
4009d4: e8 cd 00 00 00 callq 400aa6 <A::operator delete(void*)>
4009d9: b8 00 00 00 00 mov $0x0,%eax
4009de: eb 16 jmp 4009f6 <main+0x5a>
//这些死代码是什么, 当有exception时,控制流会回到这里?//
4009e0: 49 89 c4 mov %rax,%r12
4009e3: 48 89 df mov %rbx,%rdi
4009e6: e8 bb 00 00 00 callq 400aa6 <A::operator delete(void*)>
4009eb: 4c 89 e0 mov %r12,%rax
4009ee: 48 89 c7 mov %rax,%rdi
4009f1: e8 8a fe ff ff callq 400880 <_Unwind_Resume@plt>
4009f6: 48 83 c4 10 add $0x10,%rsp
4009fa: 5b pop %rbx
4009fb: 41 5c pop %r12
4009fd: 5d pop %rbp
4009fe: c3 retq
貌似跟operator delete关系不是很大。。。其实就是想听下,这些看起来是死代码的部分是怎么执行的?
下面是编译器版本和机器信息
lxb@c12:~/workspace/cpp/test$ gcc --version
gcc (GCC) 4.7.0
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
lxb@c12:~/workspace/cpp/test$ uname -a
Linux c12 2.6.32-5-amd64 #1 SMP Tue May 13 16:34:35 UTC 2014 x86_64 GNU/Linux
lxb@c12:~/workspace/cpp/test$
这是一条镜像帖。来源:北邮人论坛 / cpp / #90356同步于 2016/2/24
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
[问题]operator delete
zx723
2016/2/24镜像同步11 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
这个_Unwind_Resume看上去像是异常处理用的。但是我说不清为什么这个程序的main函数需要在异常处理中对对象执行delete。不应该啊。
至于异常处理如何工作,当抛出异常(throw)的时候,标准库(可能是libgcc、libstdc++或者libc++)里面会提供一些函数用来unwind这个栈,也就是从顶到底扫描各个frame。编译器早在编译一个程序的时候就会生成一些元数据,linux上的话,会放在一个叫.eh_frame的段里。在unwind的过程中会查阅,主要是用于恢复寄存器和栈的状态,以及跳到正确的位置继续执行。那段看上去“死”了的代码,有可能就是异常处理、unwind的时候,通过查阅相关的元数据,而跳过去的。
【 在 nuanyangyang 的大作中提到: 】
: 这个_Unwind_Resume看上去像是异常处理用的。但是我说不清为什么这个程序的main函数需要在异常处理中对对象执行delete。不应该啊。
: 至于异常处理如何工作,当抛出异常(throw)的时候,标准库(可能是libgcc、libstdc++或者libc++)里面会提供一些函数用来unwind这个栈,也就是从顶到底扫描各个frame。编译器早在编译一个程序的时候就会生成一些元数据,linux上的话,会放在一个叫.eh_frame的段里。在unwind的过程中会查阅,主要是用于恢复寄存器和栈的状态,以及跳到正确的位置继续执行。那段看上去“死”了的代码,有可能就是异常处理、unwind的时候,通过查阅相关的元数据,而跳过去的。
unwind时,不是要对栈上的对象进行“清理”嘛?对于正常的对象应该就是调用析构函数,然后调用operator delete,对于不完整的对象,应该是只调用operator delete吧。
eh_frame这个东西之前看过一点的,师姐现在说的这个异常处理过程,我大致明白。
【 在 zx723 的大作中提到: 】
:
: unwind时,不是要对栈上的对象进行“清理”嘛?对于正常的对象应该就是调用析构函数,然后调用operator delete,对于不完整的对象,应该是只调用operator delete吧。
: eh_frame这个东西之前看过一点的,师姐现在说的这个异常处理过程,我大致明白。
栈上对象的清理指的是有auto生存期的对象,也就是局部变量。对于new出来的对象,应该只在调用delete的时候清理,不应该自动清理的。
void foo() {
A a1; // 函数结束会清理
A *a2 = new A(); // 不会清理
unique_ptr<A> a3 = make_unique<A>(); // a3这个unique_ptr会被清理,清理的过程中导致里面指向的对象被delete。
}
【 在 nuanyangyang 的大作中提到: 】
:
: 栈上对象的清理指的是有auto生存期的对象,也就是局部变量。对于new出来的对象,应该只在调用delete的时候清理,不应该自动清理的。
:
: ...................
师姐说的是函数正常退出的情况,如果在调用类的构造函数初始化对象时发生了异常,编译器有责任回收operator new分配的内存,也就是调用operator delete,否则就memory leak啊。
请看下面程序:
#include <iostream>
using namespace std;
struct A{
A(int x) {
cout << "A(" << this << ")" << endl;
if (x) throw 0;
}
void operator delete(void *ptr) {
cout << "delete" << endl;
::operator delete(ptr);
}
void * operator new(size_t n) {
cout << "new" << endl;
return ::operator new(n);
}
~A() { cout << "~A(" << this << ")" << endl;}
};
void baz() {
int *p = new int;
}
void bar() {
A a(0); //stack unwind时析构
baz();
}
void foo() {
bar();
A *p = new A(1); // 构造对象时发生了异常,需要调用operator delete回收内存
}
int main(void) {
try {
foo();
} catch (int x) {
cout << x << endl;
}
return 0;
}
运行结果:
A(0x7fffecff3c8f)
~A(0x7fffecff3c8f)
new
A(0x17be030)
delete
0
【 在 zx723 的大作中提到: 】
:
: 师姐说的是函数正常退出的情况,如果在调用类的构造函数初始化对象时发生了异常,编译器有责任回收operator new分配的内存,也就是调用operator delete,否则就memory leak啊。
: 请看下面程序:
: ...................
抱歉刚才搞错了。如果new成功但构造函数A()抛出异常,那么析构函数~A()不会调用,但delete是会做的。所以那段看似死了的代码应该是处理构造函数里抛出的异常的,只是它不执行析构函数,仅仅delete刚分配出的空间,然后让异常继续向下扩散。
这里有叙述: http://en.cppreference.com/w/cpp/language/new
暖神不是师姐
【 在 zx723 的大作中提到: 】
:
: 师姐说的是函数正常退出的情况,如果在调用类的构造函数初始化对象时发生了异常,编译器有责任回收operator new分配的内存,也就是调用operator delete,否则就memory leak啊。
: 请看下面程序:
: ...................