面试题答案
一键面试delete
和 delete []
与内存分配器交互的差异
delete
:delete
用于释放单个对象的内存。在C++ 底层,它首先调用对象的析构函数(如果有),然后调用内存分配器的释放函数来释放该对象占用的内存空间。例如,对于MyClass* obj = new MyClass(); delete obj;
,会先调用obj
的析构函数~MyClass()
,然后调用内存分配器将obj
所指向的内存块归还给堆。
delete []
:delete []
用于释放数组对象的内存。它会对数组中的每个元素调用析构函数(如果有),然后调用内存分配器释放整个数组占用的连续内存空间。对于MyClass* arr = new MyClass[10]; delete [] arr;
,会依次调用arr[0]
到arr[9]
的析构函数~MyClass()
,然后调用内存分配器释放这 10 个MyClass
对象占用的连续内存块。
确保自定义内存分配器正确释放内存及调用析构函数的实现思路
- 重载全局
operator new
和operator delete
:为了让自定义内存分配器MyAllocator
能被delete
和delete []
正确调用,需要重载全局的operator new
和operator delete
函数。 - 跟踪对象数量(针对
delete []
):在分配数组内存时,需要额外记录数组中对象的数量,以便在delete []
时正确调用每个对象的析构函数。一种常见的做法是在分配的内存块头部存储对象数量。
关键代码片段
#include <iostream>
class MyAllocator {
public:
void* allocate(size_t size) {
// 实际的内存分配逻辑,例如调用系统的malloc
return ::operator new(size);
}
void deallocate(void* ptr) {
// 实际的内存释放逻辑,例如调用系统的free
::operator delete(ptr);
}
};
MyAllocator myAllocator;
// 重载全局 operator new
void* operator new(size_t size) {
return myAllocator.allocate(size);
}
// 重载全局 operator delete
void operator delete(void* ptr) {
myAllocator.deallocate(ptr);
}
// 重载全局 operator new[]
void* operator new[](size_t size) {
// 这里额外分配4字节用于存储对象数量(假设是32位系统,4字节足够存储数组元素数量)
size_t totalSize = size + sizeof(size_t);
void* ptr = myAllocator.allocate(totalSize);
// 在分配内存块头部存储对象数量
*(size_t*)ptr = size / sizeof(char); // 简单假设对象大小为1字节,实际应根据对象类型计算
return static_cast<char*>(ptr) + sizeof(size_t);
}
// 重载全局 operator delete[]
void operator delete[](void* ptr) {
// 计算出存储对象数量的位置
char* realPtr = static_cast<char*>(ptr) - sizeof(size_t);
size_t numElements = *(size_t*)realPtr;
// 调用每个对象的析构函数
for (size_t i = 0; i < numElements; ++i) {
(static_cast<char*>(ptr) + i * sizeof(char))->~char(); // 假设对象类型为char,实际应根据对象类型调用析构函数
}
myAllocator.deallocate(realPtr);
}
class MyClass {
public:
MyClass() { std::cout << "MyClass constructor" << std::endl; }
~MyClass() { std::cout << "MyClass destructor" << std::endl; }
};
int main() {
MyClass* obj = new MyClass();
delete obj;
MyClass* arr = new MyClass[3];
delete [] arr;
return 0;
}
上述代码展示了如何通过重载全局的 operator new
和 operator delete
函数,让自定义内存分配器 MyAllocator
能够在 delete
和 delete []
操作中正确释放内存并调用析构函数。注意在实际应用中,对于 operator new[]
和 operator delete[]
中对象大小及析构函数调用的处理应根据实际对象类型准确计算和操作。