这些额外的 8 个字节用于存储有关已分配内容的信息,以便正确销毁对象(程序需要知道需要销毁多少对象)并调用T::operator delete[]
使用正确的第二个参数。根据生成的程序集(见本答案末尾),存储的值是元素的数量(这里10
).
基本上:
for A
and B
,析构函数是无操作的,因此不需要知道必须销毁多少元素,并且您没有用户定义的delete[]
,所以编译器会使用默认的,它显然不关心第二个参数;
for C
,析构函数是使用定义的,所以必须调用它(我不知道为什么这没有优化...),所以程序需要知道有多少个对象将被销毁;
for D
,你有一个用户定义的D::operator delete[]
,因此程序必须记住分配的大小才能将其发送到D::operator delete[]
必要时。
如果您更换int
具有非平凡析构函数的类型的属性(例如std::vector<int>
),你会注意到这 8 个字节A
and B
.
您可以查看生成的程序集C
(g++ 7.2,无优化):
; C *c = new C[10];
call C::operator new[](unsigned long)
mov QWORD PTR [rax], 10 ; store "10" (allocated objects)
add rax, 8 ; increase pointer by 8
mov QWORD PTR [rbp-24], rax
; delete[] c;
cmp QWORD PTR [rbp-24], 0
je .L5
mov rax, QWORD PTR [rbp-24] ; this is c
sub rax, 8
mov rax, QWORD PTR [rax] ; retrieve the number of objects
lea rdx, [0+rax*4] ; retrieve the associated size (* sizeof(C))
mov rax, QWORD PTR [rbp-24]
lea rbx, [rdx+rax]
.L7:
cmp rbx, QWORD PTR [rbp-24] ; loops to destruct allocated objects
je .L6
sub rbx, 4
mov rdi, rbx
call C::~C()
jmp .L7
.L6:
mov rax, QWORD PTR [rbp-24]
sub rax, 8
mov rax, QWORD PTR [rax] ; retrieve the number of allocated objects
add rax, 2 ; add 2 = 8 bytes / sizeof(C)
lea rdx, [0+rax*4] ; number of allocated bytes
mov rax, QWORD PTR [rbp-24]
sub rax, 8
mov rsi, rdx
mov rdi, rax
call operator delete[](void*, unsigned long)
如果您不熟悉汇编,这里有一个经过整理的 C++ 版本,说明了幕后发生的情况:
// C *c = new C[10];
char *c_ = (char*)malloc(10 * sizeof(C) + sizeof(std::size_t)); // inside C::operator new[]
*reinterpret_cast<std::size_t*>(c_) = 10; // stores the number of allocated objects
C *c = (C*)(c_ + sizeof(std::size_t)); // retrieve the "correct" pointer
// delete[] c; -- destruction of the allocated objects
char *c_ = (char*)c;
c_ -= sizeof(std::size_t); // retrieve the original pointer
std::size_t n = // retrieve the number of allocated objects
*reinterpret_cast<std::size_t*>(c_);
n = n * sizeof(C); // = n * 4, retrieve the allocated size
c_ = (char*)c + n; // retrieve the "end" pointer
while (c_ != (char*)c) {
c_ -= sizeof(C); // next object
(*reinterpret_cast<C*>(c_)).~C(); // destruct the object
}
// delete[] c; -- freeing of the memory
char *c_ = (char*)c;
c_ -= sizeof(std::size_t);
std::size_t n =
*reinterpret_cast<std::size_t*>(c_); // retrieve the number of allocated objects
n = n * sizeof(C) + sizeof(std::size_t); // note: compiler does funky computation instead of
// this, but I found this clearer
::operator delete[](c_, n);
现在您很高兴知道编译器为您完成了所有这些工作;)