BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / cpp / #90356同步于 2016/2/24
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖

[问题]operator delete

zx723
2016/2/24镜像同步11 回复
又来麻烦贵邮大神了,看程序时,意外看到编译器为我生成的代码,没有看明白。请指教 程序源码如下: #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$
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
nuanyangyang机器人#1 · 2016/2/24
这个_Unwind_Resume看上去像是异常处理用的。但是我说不清为什么这个程序的main函数需要在异常处理中对对象执行delete。不应该啊。 至于异常处理如何工作,当抛出异常(throw)的时候,标准库(可能是libgcc、libstdc++或者libc++)里面会提供一些函数用来unwind这个栈,也就是从顶到底扫描各个frame。编译器早在编译一个程序的时候就会生成一些元数据,linux上的话,会放在一个叫.eh_frame的段里。在unwind的过程中会查阅,主要是用于恢复寄存器和栈的状态,以及跳到正确的位置继续执行。那段看上去“死”了的代码,有可能就是异常处理、unwind的时候,通过查阅相关的元数据,而跳过去的。
zx723机器人#2 · 2016/2/25
【 在 nuanyangyang 的大作中提到: 】 : 这个_Unwind_Resume看上去像是异常处理用的。但是我说不清为什么这个程序的main函数需要在异常处理中对对象执行delete。不应该啊。 : 至于异常处理如何工作,当抛出异常(throw)的时候,标准库(可能是libgcc、libstdc++或者libc++)里面会提供一些函数用来unwind这个栈,也就是从顶到底扫描各个frame。编译器早在编译一个程序的时候就会生成一些元数据,linux上的话,会放在一个叫.eh_frame的段里。在unwind的过程中会查阅,主要是用于恢复寄存器和栈的状态,以及跳到正确的位置继续执行。那段看上去“死”了的代码,有可能就是异常处理、unwind的时候,通过查阅相关的元数据,而跳过去的。 unwind时,不是要对栈上的对象进行“清理”嘛?对于正常的对象应该就是调用析构函数,然后调用operator delete,对于不完整的对象,应该是只调用operator delete吧。 eh_frame这个东西之前看过一点的,师姐现在说的这个异常处理过程,我大致明白。
nuanyangyang机器人#3 · 2016/2/25
【 在 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。 }
zx723机器人#4 · 2016/2/25
【 在 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
FromMars机器人#5 · 2016/2/25
进来学习一下
zx723机器人#6 · 2016/2/25
【 在 FromMars 的大作中提到: 】 : 进来学习一下 谢谢,要是能给些意见建议就更好了
nuanyangyang机器人#7 · 2016/2/25
【 在 zx723 的大作中提到: 】 : : 师姐说的是函数正常退出的情况,如果在调用类的构造函数初始化对象时发生了异常,编译器有责任回收operator new分配的内存,也就是调用operator delete,否则就memory leak啊。 : 请看下面程序: : ................... 抱歉刚才搞错了。如果new成功但构造函数A()抛出异常,那么析构函数~A()不会调用,但delete是会做的。所以那段看似死了的代码应该是处理构造函数里抛出的异常的,只是它不执行析构函数,仅仅delete刚分配出的空间,然后让异常继续向下扩散。 这里有叙述: http://en.cppreference.com/w/cpp/language/new
xiaobing307机器人#8 · 2016/2/25
暖神不是师姐 【 在 zx723 的大作中提到: 】 : : 师姐说的是函数正常退出的情况,如果在调用类的构造函数初始化对象时发生了异常,编译器有责任回收operator new分配的内存,也就是调用operator delete,否则就memory leak啊。 : 请看下面程序: : ...................
zx723机器人#9 · 2016/2/25
【 在 xiaobing307 的大作中提到: 】 : 暖神不是师姐 那暖神是什么?师兄?