Vector Deleting Destructor
Today one guy in my team asked a question regarding the behavior of delete[] operator in C++ - how does the program know it needs to call CBar::~CBar instead of CFoo::~CFoo?
Note that the vector deleting destructor is a feature in Microsoft C++ Compiler, not required by the C++ standard.
#define _CRTDBG_MAP_ALLOC
#include <malloc.h>
#include <crtdbg.h>
class CFoo
{
public:
virtual ~CFoo() = 0;
};
class CBar
{
virtual ~CBar()
{
_CrtDbgBreak();
}
};
void __cdecl main()
{
CBar* p = new CBar[1];
delete[] (CFoo*)p; // what does this mean?
_CrtDumpMemoryLeaks();
}
Well the short answer would be - how does virtual destructor work and how does the program know the actual number of elements in the array?
If you've spent time on understanding the object layout in C++ and COM, you would know that virtual functions are backed by vtable, and it will not be too difficult to understand BSTR.
So we are ready for the answer - delete[] is a combination of BSTR and virtual function.
CBar::`vector deleting destructor':
000007F7CD2E1140 mov dword ptr [rsp+10h],edx
000007F7CD2E1144 mov qword ptr [rsp+8],rcx
000007F7CD2E1149 sub rsp,28h
000007F7CD2E114D mov eax,dword ptr [rsp+38h]
000007F7CD2E1151 and eax,2
000007F7CD2E1154 test eax,eax
000007F7CD2E1156 je CBar::`vector deleting destructor'+5Eh (07F7CD2E119Eh)
000007F7CD2E1158 lea r9,[CBar::~CBar (07F7CD2E1120h)] ; the address of CBar::~CBar
000007F7CD2E115F mov rax,qword ptr [this] ; this pointer
000007F7CD2E1164 mov r8d,dword ptr [rax-8] ; array size *((size_t*)this - 1)
000007F7CD2E1168 mov edx,8 ; sizeof(CBar)
000007F7CD2E116D mov rcx,qword ptr [this] ; this pointer
000007F7CD2E1172 call `vector destructor iterator' (07F7CD2E1220h)
As you can see here, the vector deleting destructor was emitted as a virtual function in vtable, it takes a flag parameter, if flag & 0x2 equals to true, the actual function vector destructor iterator would get called.
The size of array was stored using the BSTR approach, passed in via the r8d register.
The callstack from Visual Studio Debugger also tells us the same thing:
crtdbg.exe!`vector destructor iterator'(void * __t, unsigned __int64 __s, int __n, void (void *) * __f) C++
crtdbg.exe!CBar::`vector deleting destructor'(unsigned int) C++
crtdbg.exe!main() Line 22 C++
crtdbg.exe!__tmainCRTStartup() Line 536 C
crtdbg.exe!mainCRTStartup() Line 377 C
kernel32.dll!BaseThreadInitThunk() Unknown
ntdll.dll!RtlUserThreadStart() Unknown
Regarding the actual meaning of the flag I mentioned, I'll leave it as a homework for the readers (hint: you may try out the DGML tool).