为何C++编译器要为有虚函数的类生成给new[]/delete[]用的vector ctor/dtor?解决办法
为何C++编译器要为有虚函数的类生成给new[]/delete[]用的vector ctor/dtor?
最近在研究new/delete对象数组的时候,编译器生成的代码,发现很奇怪的地方:
例如,我有下面这一小段代码:
我在VC下面用/d1ReportAllClassLayout会在output窗口里面得到各个类的对象模型:
1>class O size(4):
1> +---
1> 0 | o
1> +---
1>class A size(8):
1> +---
1> 0 | {vfptr}
1> 4 | a
1> +---
1>A::$vftable@:
1> | &A_meta
1> | 0
1> 0 | &A::{dtor}
1>A::{dtor} this adjustor: 0
1>A::__delDtor this adjustor: 0
1>A::__vecDelDtor this adjustor: 0
1>class B size(12):
1> +---
1> 0 | {vbptr}
1> +---
1> +--- (virtual base A)
1> 4 | {vfptr}
1> 8 | a
1> +---
1>B::$vbtable@:
1> 0 | 0
1> 1 | 4 (Bd(B+0)A)
1>B::$vftable@:
1> | -4
1> 0 | &B::{dtor}
1>B::{dtor} this adjustor: 4
1>B::__delDtor this adjustor: 4
1>B::__vecDelDtor this adjustor: 4
1>vbi: class offset o.vbptr o.vbte fVtorDisp
1> A 4 0 4 0
--------------------
问题:
(1) 为什么要为A和B生成vector ctor和dtor:
1>A::{dtor} this adjustor: 0
1>A::__delDtor this adjustor: 0
1>A::__vecDelDtor this adjustor: 0
1>B::{dtor} this adjustor: 4
1>B::__delDtor this adjustor: 4
1>B::__vecDelDtor this adjustor: 4
既然析构函数已经是虚函数了,对象实例的虚表指针就能指向正确的虚表位置,调用正确的dtor来做析构。
为什么要生成DelDtor这样的函数呢? 用ecx计数器来循环调用ctor和dtor不行么? 非要弄个函数出来?
(2) 我跟踪了一下反汇编的代码:
O*po=new O[3]没有调用vector ctor.
delete[] po调用了vector dtor.
A*pa=new A[3]先做了件奇怪的事情:
push offset A::~A()
push offset A::A()
(2.1)上面这两个push到底在干什么?
然后调用了vector ctor: call 'eh vector constructor iterator'
(2.2)ecx计数循环调用ctor不就行了,干嘛弄个vector ctor iterator出来?
(2.3)为什么A类型有调用vector ctor,而O类型没有?
delete[] pa的时候调用了vector dtor.
(3)B* pb=new B[3];
push offset B::'vbase desstructor' // (3.1)这里更奇怪了,push基类的dtor,岂不是要故意造成内存泄漏,因为delete调用的是基类的dtor啊
push offset B::B
...
call 'eh vector base constructor iterator' // (3.2)怎么还是调用基类的ctor? 没有看件另一行call指令来调用B类的ctor啊。难道B类自身不构造了?
delete []pb;
我发现汇编的实现是Push 3以后干了一堆事情然后call operator delete[]
(3.3)这里怎么没有调用vector dtor? 而O和A类型都调用了?
------解决方案--------------------
O* pa= new A[3];
delete[] pa;
这种代码是错误的。
只能
A* pa= new A[3];
delete[] pa;
所以编译器为这种情况生成一个特定的 虚构函数没啥奇怪的。
------解决方案--------------------
------解决方案--------------------
------解决方案--------------------
最近在研究new/delete对象数组的时候,编译器生成的代码,发现很奇怪的地方:
例如,我有下面这一小段代码:
- C/C++ code
struct O{ int o; ~O(){} }; struct A { int a; virtual ~A(){} }; struct B:virtual A{ int b; virtual ~B(){} }; void main(void){ O* po= new O[3]; delete[] po; A* pa= new A[3]; delete[] pa; B* pb= new B[3]; delete[] pb; }
我在VC下面用/d1ReportAllClassLayout会在output窗口里面得到各个类的对象模型:
1>class O size(4):
1> +---
1> 0 | o
1> +---
1>class A size(8):
1> +---
1> 0 | {vfptr}
1> 4 | a
1> +---
1>A::$vftable@:
1> | &A_meta
1> | 0
1> 0 | &A::{dtor}
1>A::{dtor} this adjustor: 0
1>A::__delDtor this adjustor: 0
1>A::__vecDelDtor this adjustor: 0
1>class B size(12):
1> +---
1> 0 | {vbptr}
1> +---
1> +--- (virtual base A)
1> 4 | {vfptr}
1> 8 | a
1> +---
1>B::$vbtable@:
1> 0 | 0
1> 1 | 4 (Bd(B+0)A)
1>B::$vftable@:
1> | -4
1> 0 | &B::{dtor}
1>B::{dtor} this adjustor: 4
1>B::__delDtor this adjustor: 4
1>B::__vecDelDtor this adjustor: 4
1>vbi: class offset o.vbptr o.vbte fVtorDisp
1> A 4 0 4 0
--------------------
问题:
(1) 为什么要为A和B生成vector ctor和dtor:
1>A::{dtor} this adjustor: 0
1>A::__delDtor this adjustor: 0
1>A::__vecDelDtor this adjustor: 0
1>B::{dtor} this adjustor: 4
1>B::__delDtor this adjustor: 4
1>B::__vecDelDtor this adjustor: 4
既然析构函数已经是虚函数了,对象实例的虚表指针就能指向正确的虚表位置,调用正确的dtor来做析构。
为什么要生成DelDtor这样的函数呢? 用ecx计数器来循环调用ctor和dtor不行么? 非要弄个函数出来?
(2) 我跟踪了一下反汇编的代码:
O*po=new O[3]没有调用vector ctor.
delete[] po调用了vector dtor.
A*pa=new A[3]先做了件奇怪的事情:
push offset A::~A()
push offset A::A()
(2.1)上面这两个push到底在干什么?
然后调用了vector ctor: call 'eh vector constructor iterator'
(2.2)ecx计数循环调用ctor不就行了,干嘛弄个vector ctor iterator出来?
(2.3)为什么A类型有调用vector ctor,而O类型没有?
delete[] pa的时候调用了vector dtor.
(3)B* pb=new B[3];
push offset B::'vbase desstructor' // (3.1)这里更奇怪了,push基类的dtor,岂不是要故意造成内存泄漏,因为delete调用的是基类的dtor啊
push offset B::B
...
call 'eh vector base constructor iterator' // (3.2)怎么还是调用基类的ctor? 没有看件另一行call指令来调用B类的ctor啊。难道B类自身不构造了?
delete []pb;
我发现汇编的实现是Push 3以后干了一堆事情然后call operator delete[]
(3.3)这里怎么没有调用vector dtor? 而O和A类型都调用了?
------解决方案--------------------
O* pa= new A[3];
delete[] pa;
这种代码是错误的。
只能
A* pa= new A[3];
delete[] pa;
所以编译器为这种情况生成一个特定的 虚构函数没啥奇怪的。
------解决方案--------------------
------解决方案--------------------
------解决方案--------------------