重载operator delete[]与析构函数的关联问题解析
我实现了一个TvectorPublic类,通过重载operator new[]和operator delete[]来追踪内存分配与释放情况:
class TvectorPublic { public: int x, y, z; TvectorPublic(int x_1 = 0,int y_1 = 0,int z_1 = 0) : x(x_1),y(y_1),z(z_1) {} void m_DoubleValue() { x *= 2; y *= 2; z *= 2; } void m_AddingValue(int value) { x += value; y += value; z += value; } void* operator new[](size_t size_1) { std::cout << "Creating TvectorPublic object.\n"; std::cout << "Reserving memory: " << size_1 << " bytes.\n"; return (::new char[size_1]); } void operator delete[](void* pointer, size_t size_1) noexcept { if (pointer) { std::cout << "\nDeleting TvectorPublic object.\n"; std::cout << "Freeing memory: " << size_1 << " bytes.\n"; ::delete[] static_cast<char*>(pointer); } } };
在main函数中创建该类的对象数组:
TvectorPublic* pointer_1 = new TvectorPublic[3]; std::cout << "\n"; for (int i = 0; i < 3; i++) { std::cout << pointer_1[i] << "\n"; } std::cout << "\n"; delete[] pointer_1;
运行后发现:分配的内存为36字节,但仅释放了12字节;当为类添加如下空析构函数后:
~TvectorPublic() { std::cout << "DestructorCalled\n"; }
分配和释放的内存均变为40字节,异常消失。以下是针对问题的解答:
1. 未定义析构函数时为何出现内存泄漏?编译器为何不生成默认析构函数?
先澄清一个误区:编译器会生成默认析构函数,这里的问题和析构函数是否生成无关,而是数组delete[]调用重载函数时的参数匹配问题。
当类里只有内置类型(比如这里的int),没有用户定义的析构函数时,编译器认为不需要逐个调用对象的析构函数——反正析构也不会做任何操作。这时候new[]分配的就是3个对象的总大小:3 * sizeof(TvectorPublic) = 3*12=36字节。但调用delete[]时,编译器没法正确把总内存大小传递给你重载的带size_t参数的operator delete[],它可能只传递了单个对象的大小12字节,导致你追踪到的释放大小是12,但实际上全局的delete[]还是释放了整个36字节的内存,并没有真的泄漏,只是追踪显示的数值错了。
2. 添加空析构函数后,内存占用为何增加4字节?
当类有用户定义的析构函数时,new[]必须额外分配一块内存来存数组元素的个数(32位系统下是4字节,64位是8字节)——因为delete[]需要知道要调用多少次析构函数。所以总内存大小就变成了:4字节计数 + 3*12字节对象 = 40字节,这就是你看到内存多了4字节的原因。
3. 为何只有添加析构函数后,全部内存才会被释放?(创建单个对象时无此异常)
单个对象的new/delete不需要记录元素个数,所以不管有没有析构函数,operator new/delete拿到的参数都是单个对象的正确大小,不会出问题。
而数组场景下:
- 没有用户析构函数时,编译器不需要那个计数头,
new[]直接分配36字节,但delete[]调用你重载的带size_t参数的版本时,没法正确传递总内存大小,只能传单个对象的12字节,导致你追踪到的释放数值不对,但实际内存没漏。 - 有用户析构函数时,
new[]分配了带计数头的40字节,delete[]通过计数头能算出总内存大小,把40正确传递给你的重载函数,所以追踪显示的分配和释放大小一致,看起来就像“全部内存被释放”了。
另外补充:C++标准里,带size_t参数的operator delete[],只有编译器能确定内存块大小时才会传递正确的数值。有析构函数的数组场景,编译器能通过计数头拿到总大小;没析构函数的数组场景,编译器没法准确获取,就传递了错误的size值。
内容的提问来源于stack exchange,提问作者Jakub

