c++ 析构函数为虚函数的问题
来源:互联网 发布:java父类指向子类 编辑:程序博客网 时间:2024/06/01 08:25
昨天去XX公司面试,面试官问了一个关于C++类析构函数为虚函数时,如果是父类的指针用子类来new,如果发生析构时,析构函数是virtual与不是virtual有什么区别。当时答的不好,回来总结了一下,在机器上实现了一遍,终于搞明白了。记录下来,以后遇到这种情况自己一定不要犯错了
一、先看第一种最简单的情况吧,教科书上教的,析构函数不是virtual,正常定义一个子类对象
class student { public: int *m_pInt; student() { m_pInt = new int[10]; //1 memset(m_pInt, 0, 10*4); } ~student() { //3 delete []m_pInt; } }; class GradeOneStue:public student { public: int m_iNum; GradeOneStue() { //2 m_iNum = 1; } ~GradeOneStue() { //4 m_iNum = 0; } }; int _tmain(int argc, _TCHAR* argv[]) { GradeOneStue gd; return 0; }
这时构造顺序是先1后2,下面是反汇编代码
GradeOneStue() 00411470 push ebp 00411471 mov ebp,esp 00411473 sub esp,0CCh 00411479 push ebx 0041147A push esi 0041147B push edi 0041147C push ecx 0041147D lea edi,[ebp-0CCh] 00411483 mov ecx,33h 00411488 mov eax,0CCCCCCCCh 0041148D rep stos dword ptr es:[edi] 0041148F pop ecx 00411490 mov dword ptr [ebp-8],ecx 00411493 mov ecx,dword ptr [this] 00411496 call student::student (411109h) { //2 m_iNum = 1; 0041149B mov eax,dword ptr [this] 0041149E mov dword ptr [eax+4],1 }
再来看看析构时的顺序(教科书上写的是先调用子类的析构函数,在调用父类的,与构造过程相反)
~GradeOneStue() { //4 00411550 push ebp 00411551 mov ebp,esp 00411553 sub esp,0CCh 00411559 push ebx 0041155A push esi 0041155B push edi 0041155C push ecx 0041155D lea edi,[ebp-0CCh] 00411563 mov ecx,33h 00411568 mov eax,0CCCCCCCCh 0041156D rep stos dword ptr es:[edi] 0041156F pop ecx 00411570 mov dword ptr [ebp-8],ecx m_iNum = 0; 00411573 mov eax,dword ptr [this] 00411576 mov dword ptr [eax+4],0 } 0041157D mov ecx,dword ptr [this] 00411580 call student::~student (41102Dh)
可以看到顺序和教科书上一样。
二、析构函数是virtual,正常定义一个子类对象
构造函数顺序就略过了,看析构汇编代码
virtual ~GradeOneStue() { //4 004116D0 push ebp 004116D1 mov ebp,esp ... 004116F3 mov eax,dword ptr [this] 004116F6 mov dword ptr [eax],offset GradeOneStue::`vftable' (415640h) m_iNum = 0; 004116FC mov eax,dword ptr [this] 004116FF mov dword ptr [eax+8],0 } 00411706 mov ecx,dword ptr [this] 00411709 call student::~student (411091h) ...
三、用一个父类指针去new一个子类对象,析构函数不是virtual,构造过程略过
class student { public: int *m_pInt; student() { m_pInt = new int[10]; //1 memset(m_pInt, 0, 10*4); } ~student() { //3 delete []m_pInt; } }; class GradeOneStue:public student { public: int m_iNum; GradeOneStue() { //2 m_iNum = 1; } ~GradeOneStue() { //4 m_iNum = 0; } }; int _tmain(int argc, _TCHAR* argv[]) { student *pStu = new GradeOneStue(); delete pStu; return 0; }
delete pStu; 00413726 mov eax,dword ptr [ebp-14h] 00413729 mov dword ptr [ebp-0E0h],eax 0041372F mov ecx,dword ptr [ebp-0E0h] 00413735 mov dword ptr [ebp-0ECh],ecx 0041373B cmp dword ptr [ebp-0ECh],0 00413742 je wmain+0C9h (413759h) 00413744 push 1 00413746 mov ecx,dword ptr [ebp-0ECh] 0041374C call student::`scalar deleting destructor' (4111E5h) 00413751 mov dword ptr [ebp-10Ch],eax 00413757 jmp wmain+0D3h (413763h) 00413759 mov dword ptr [ebp-10Ch],0 return 0;
看到只调用了父类的析构函数,此时子类的析构函数没有被调用,此时子类的析构函数中虽然有调用父类析构函数的代码,但是这里直接调用的是父类的析构函数,所以这是如果子类中析构函数有释放资源的代码,这里会造成这部分资源不被释放,有可能造成内存泄露
四、用一个父类指针去new一个子类对象,析构函数是virtual,构造过程略过
这里直接看delete pStu的汇编代码
delete pStu; 004114E6 mov eax,dword ptr [ebp-14h] 004114E9 mov dword ptr [ebp-0E0h],eax 004114EF mov ecx,dword ptr [ebp-0E0h] 004114F5 mov dword ptr [ebp-0ECh],ecx 004114FB cmp dword ptr [ebp-0ECh],0 00411502 je wmain+0D9h (411529h) 00411504 mov esi,esp 00411506 push 1 00411508 mov edx,dword ptr [ebp-0ECh] //edx等于pStu,指向new出来的子类对象 0041150E mov eax,dword ptr [edx] //将edx指向的dword传给eax,eax现在是保存的虚函数表指向的地址 00411510 mov ecx,dword ptr [ebp-0ECh] 00411516 mov edx,dword ptr [eax] //将虚函数表指向的第一个函数的地址传给edx,也就是唯一的一个虚析构函数的地址 00411518 call edx //调用析构函数,这个函数是子类的,这个地址构造时写入虚函数表
最后的结论:
1、无论父类与子类的析构函数是否是virtual,子类的析构函数都会调用父类的析构函数
2、如果父类与子类的析构函数不为virtual,用一个父类指针指向一个用子类类型new的对象,delete时,直接调用父类的析构函数,这是在编译时刻就决定的。如果子类析构函数中有释放资源的代码,这是会发生资源泄漏。
3、如果父类与子类的析构函数是virtual,用一个父类指针指向一个用子类类型new的对象,delete时,这时由于是通过虚函数表调用析构函数,而虚函数表中的地址是构造时写入的,是子类的析构函数的地址,由于结论第一条,所以子类与父类的析构函数都会得到调用,不会发生资源泄漏。
转自:http://blog.sina.com.cn/s/blog_94021710010152cp.html
- c++ 析构函数为虚函数的问题
- c++ 析构函数为虚函数的问题
- 构造函数、析构函数是否要声明为虚函数的问题
- 构造函数、析构函数是否要声明为虚函数的问题(网络转载)
- 构造函数、析构函数是否要声明为虚函数的问题
- 构造函数、析构函数是否要声明为虚函数的问题
- 构造函数、析构函数为虚函数的区别
- C/C++沉思-----多态时一定要将父类(基类)的析构函数定义为虚函数
- C/C++沉思-----多态时一定要将父类(基类)的析构函数定义为虚函数
- C/C++沉思-----多态时一定要将父类(基类)的析构函数定义为虚函数
- C/C++沉思-----多态时一定要将父类(基类)的析构函数定义为虚函数
- C/C++基类的析构函数为什么要为虚函数
- (1)为什么构造函数的名字用类名?(2)构造函数返回值问题;(3)析构函数和构造函数定义为虚函数;
- C++:构造函数和析构函数能否为虚函数
- C++:构造函数和析构函数能否为虚函数
- C++:构造函数和析构函数能否为虚函数
- C++:构造函数和析构函数能否为虚函数
- C++:构造函数和析构函数能否为虚函数
- P2P中DHT网络介绍
- zabbix之通过jmx监控tomcat
- Android FrameWork浅识
- 自定义CoordinatorLayout的Behavior实现知乎和简书快速返回效果
- Centos7 安装kilo-7 Controller(neutron)
- c++ 析构函数为虚函数的问题
- HDU 1032(3n+1问题)
- 从零开始自学Swift(八)
- error C2719: "_Val": 具有__declspec(align('16'))的形参将不被对齐
- BitTorrent的DHT协议(译自官方版本)
- Centos 6.x/7.x yum安装php5.6.X(最新版)
- HDU 1020(字符串转码)
- new一个Object对象占用多少内存?
- vad in amr codec from 3gpp