int _tmain(int argc, _TCHAR* argv[])
{
A* p = new A[10];
delete []p;
return 0;
}
下面是生成的汇编代码:
NewTest!wmain:
01181030 6a2c            push    2Ch 
01181032 e8c4030000      call    NewTest!operator new[] (011813fb) //通过operator new分配44自己
01181037 83c404          add     esp,4
0118103a 85c0            test    eax,eax
0118103c 7444            je      NewTest!wmain+0x52 (01181082)
0118103e 56              push    esi
0118103f 6810101801      push    offset NewTest!A::~A (01181010) //A的析构函数
01181044 6800111801      push    offset NewTest!A::A (01181100)  //A的构造函数
01181049 6a0a            push    0Ah //10
0118104b 8d7004          lea     esi,[eax+4] //跨过了头四个字节
0118104e 6a04            push    4    //对象大小
01181050 56              push    esi //esi里放的是对象列表的起始地址(跨过了头四个字节) 
01181051 c7000a000000    mov     dword ptr [eax],0Ah //头四个字节写入对象列表数量(10)
01181057 e812040000      call    NewTest!`eh vector constructor iterator' (0118146e)
0118105c 85f6            test    esi,esi
0118105e 7421            je      NewTest!wmain+0x51 (01181081)
01181060 837efc00        cmp     dword ptr [esi-4],0 //判断对象数量是否 为 0
01181064 8d46fc          lea     eax,[esi-4] //包含对象数量的地址保存到  eax
01181067 740f            je      NewTest!wmain+0x48 (01181078)
01181069 8b06            mov     eax,dword ptr [esi] //取A的虚表地址
0118106b 8b5004          mov     edx,dword ptr [eax+4] //虚表里的第二个函数
0118106e 6a03            push    3
01181070 8bce            mov     ecx,esi
01181072 ffd2            call    edx
01181074 5e              pop     esi
01181075 33c0            xor     eax,eax
01181077 c3              ret
 
重点看上面红色的代码, 我们可以看到, 在new一个数组时,编译器帮我们做了下面一些事情:
(1)调用数组的operator new[] 分配内存, 大小为 4 + sizeof(object) * count, 其中头四个字节为对象数量
(2)调用NewTest!`eh vector constructor iterator(pArrayAddress, sizeof(object),  object_count, pFunConstructor, pFunDestructor), 
其中 pFunDestructor为析构函数, pFunConstructor为构造函数, object_count为对象数量, sizeof(object)为对象大小,pArrayAddress为起始地址。, 
下面我们反汇编 NewTest!`eh vector constructor iterator: 
0:000> u 0118146e L50
NewTest!`eh vector constructor iterator':
0118146e 6a10            push    10h
01181470 6890221801      push    offset NewTest!__rtc_tzz+0x8 (01182290)
01181475 e8d2040000      call    NewTest!__SEH_prolog4 (0118194c)
0118147a 33c0            xor     eax,eax
0118147c 8945e0          mov     dword ptr [ebp-20h],eax
0118147f 8945fc          mov     dword ptr [ebp-4],eax
01181482 8945e4          mov     dword ptr [ebp-1Ch],eax
01181485 8b45e4          mov     eax,dword ptr [ebp-1Ch] //临时计数,初始为0
01181488 3b4510          cmp     eax,dword ptr [ebp+10h]  //将临时计数和对象数量比较
0118148b 7d13            jge     NewTest!`eh vector constructor iterator'+0x32 (011814a0) //如果临时计数大于对象数量则退出循环
0118148d 8b7508          mov     esi,dword ptr [ebp+8] //保存第一个参数(起始地址)到 esi
01181490 8bce            mov     ecx,esi //赋this指针到ecx
01181492 ff5514          call    dword ptr [ebp+14h] //调用构造函数
01181495 03750c          add     esi,dword ptr [ebp+0Ch] //移动指针, 加上对象大小
01181498 897508          mov     dword ptr [ebp+8],esi //保存新对象地址到第一个参数
0118149b ff45e4          inc     dword ptr [ebp-1Ch] //增加临时计数
0118149e ebe5            jmp     NewTest!`eh vector constructor iterator'+0x17 (01181485)
011814a0 c745e001000000  mov     dword ptr [ebp-20h],1
011814a7 c745fcfeffffff  mov     dword ptr [ebp-4],0FFFFFFFEh
011814ae e808000000      call    NewTest!`eh vector constructor iterator'+0x4d (011814bb)
011814b3 e8d9040000      call    NewTest!__SEH_epilog4 (01181991)
011814b8 c21400          ret     14h
 
我们可以看到NewTest!`eh vector constructor iterator是编译器帮我们生成的函数, 它的作用就是为数组中的每个对象都调用构造函数。
接下我们再看看数组形式的delete []在背后究竟干了什么?
重点看上面紫色的代码:
NewTest!wmain:
....
01181060 837efc00        cmp     dword ptr [esi-4],0 //判断对象数量是否 为 0
01181064 8d46fc          lea     eax,[esi-4] //包含对象数量的地址保存到  eax
01181067 740f            je      NewTest!wmain+0x48 (01181078)
01181069 8b06            mov     eax,dword ptr [esi] //取A的虚表地址
0118106b 8b5004          mov     edx,dword ptr [eax+4] //虚表里的第二个函数
0118106e 6a03            push    3
01181070 8bce            mov     ecx,esi
01181072 ffd2            call    edx
 
....
 可以看到它将对象列表起始地址保存到ecx, 然后调用对象虚表里的第二个函数, 并且传入参数是3, 我们先看对象虚表内容:
0:000> dps 01182148 
01182148  01181000 NewTest!A::print [f:\test\newtest\newtest\newtest.cpp @ 11]
0118214c  01181090 NewTest!A::`vector deleting destructor'
我们看看该函数究竟干了什么:
0:000> u 01181090  L40
NewTest!A::`vector deleting destructor':
01181090 53              push    ebx
01181091 8a5c2408        mov     bl,byte ptr [esp+8]
01181095 56              push    esi
01181096 8bf1            mov     esi,ecx
01181098 f6c302          test    bl,2 //是否需要调用析构函数
0118109b 742b            je      NewTest!A::`vector deleting destructor'+0x38 (011810c8)
0118109d 8b46fc          mov     eax,dword ptr [esi-4]
011810a0 57              push    edi
011810a1 6810101801      push    offset NewTest!A::~A (01181010)
011810a6 8d7efc          lea     edi,[esi-4]
011810a9 50              push    eax
011810aa 6a04            push    4
011810ac 56              push    esi
011810ad e87f040000      call    NewTest!`eh vector destructor iterator' (01181531)
011810b2 f6c301          test    bl,1 //是否需要释放内存
011810b5 7409            je      NewTest!A::`vector deleting destructor'+0x30 (011810c0)
011810b7 57              push    edi
011810b8 e85f030000      call    NewTest!operator delete[] (0118141c)
011810bd 83c404          add     esp,4
011810c0 8bc7            mov     eax,edi
011810c2 5f              pop     edi
011810c3 5e              pop     esi
011810c4 5b              pop     ebx
011810c5 c20400          ret     4
可以看到它内部调用的是NewTest!`eh vector destructor iterator, 而如果再跟踪NewTest!`eh vector destructor iterator,
  
会看所有数组里的对象调用析构函数, 最后调用operator delete[]释放所有内存。
我们可以看到数组new[]和delete[]的关键是, C++编译器在数组起始地址之前的4个字节保存了对象的数量N,后面会根据这个数量值进行N次的构造和析构 。 
最后申明下, 上面的分析仅限于VS2008, 实际上在符合C++标准的前提下, 各个C++编译器有各自不同的实现。
我们可以看到C++ 编译器在背后干了很多事情,可能会内联我们的函数, 也可以修改和产生其他一些函数, 而这是很多C开发者受不了的事情, 所以在内核级别, 很多人宁愿用C来减少编译器背后的干扰。
最后思考一下, 如果我们代码这样写,会怎么样? 
int _tmain(int argc, _TCHAR* argv[])
{
A* p = new B[10];
delete []p;
return 0;
}