<p>自定义内存管理器通过重载new/delete接管内存分配,实现性能优化、减少碎片、辅助调试。1. 重载全局operator new(size_t size)实现自定义分配逻辑;2. 重载operator delete(void* ptr)实现内存回收;3. 需同步处理new[]/delete[]数组版本;4. 可结合块分配器预分配大内存并切分固定大小块,通过空闲链表高效管理;5. 实际应用需注意递归调用、内存对齐、线程安全、异常处理及数组头部信息管理等陷阱。</p>

实现自定义内存管理器,核心在于接管程序的内存分配与释放机制。这通常通过重载C++的全局或类成员
new
delete

要实现一个自定义内存管理器并重载
new
delete
operator new
operator delete
new
delete
一个基础的实现思路是:

operator new(size_t size)
malloc
:** 对应地,当内存被释放时,你的
操作符会接管。你在这里实现自己的内存回收逻辑,将
以下是一个非常简化的示例,它仅仅是把内存分配和释放委托给了
malloc
free
malloc
free
#include <iostream>
#include <cstdlib> // For malloc and free
// 重载全局的 new 操作符
void* operator new(size_t size) {
std::cout << "Custom new: Allocating " << size << " bytes." << std::endl;
void* p = std::malloc(size); // 实际的内存分配
if (!p) {
throw std::bad_alloc(); // 分配失败时抛出异常
}
return p;
}
// 重载全局的 delete 操作符
void operator delete(void* p) noexcept {
std::cout << "Custom delete: Deallocating memory." << std::endl;
std::free(p); // 实际的内存释放
}
// 重载全局的 new[] 操作符 (数组版本)
void* operator new[](size_t size) {
std::cout << "Custom new[]: Allocating array of " << size << " bytes." << std::endl;
void* p = std::malloc(size);
if (!p) {
throw std::bad_alloc();
}
return p;
}
// 重载全局的 delete[] 操作符 (数组版本)
void operator delete[](void* p) noexcept {
std::cout << "Custom delete[]: Deallocating array memory." << std::endl;
std::free(p);
}
// 示例类
class MyClass {
public:
int data;
MyClass() {
std::cout << "MyClass constructor called." << std::endl;
}
~MyClass() {
std::cout << "MyClass destructor called." << std::endl;
}
};
int main() {
std::cout << "--- Testing single object allocation ---" << std::endl;
MyClass* obj = new MyClass(); // 会调用我们重载的 operator new
obj->data = 10;
std::cout << "Obj data: " << obj->data << std::endl;
delete obj; // 会调用我们重载的 operator delete
std::cout << "\n--- Testing array allocation ---" << std::endl;
MyClass* arr = new MyClass[3]; // 会调用我们重载的 operator new[]
arr[0].data = 1;
arr[1].data = 2;
arr[2].data = 3;
delete[] arr; // 会调用我们重载的 operator delete[]
std::cout << "\n--- Testing primitive type allocation ---" << std::endl;
int* i = new int; // 也会调用我们重载的 operator new
*i = 5;
delete i; // 调用我们重载的 operator delete
return 0;
}运行这段代码,你会看到自定义的
new
delete

说实话,很多人一开始觉得自定义内存管理是不是有点“过度工程”了。毕竟,标准库的
malloc
free
首先是性能瓶颈。标准的
malloc
free
new
delete
其次是内存碎片。长时间运行的程序,尤其是有大量不同大小对象频繁分配和释放的,很容易产生内存碎片。这就像你往一个箱子里装东西,尽管总空间够,但因为小缝隙太多,大件物品反而放不进去。内存碎片会导致程序无法分配到连续的大块内存,即使物理内存总量充足,最终可能导致
std::bad_alloc
再者是调试和诊断。标准库的内存分配器通常是黑盒,你很难知道内存是如何被使用的,哪里发生了内存泄漏,或者哪里出现了越界访问。通过重载
new
delete
最后,还有一些特殊需求。比如,你可能需要将特定类型的数据分配到特定的内存区域(例如GPU内存、非易失性内存),或者实现一个线程局部的内存分配器来避免全局锁。这些都是标准分配器无法提供的能力。自定义,意味着掌控一切。
new
delete
重载
new
delete
一个最常见的陷阱是递归调用。如果你在自定义的
operator new
operator delete
new
delete
operator new
std::vector
std::vector
new
malloc
free
new
delete
内存对齐也是个大问题。现代CPU为了性能,经常要求数据在内存中按照特定边界对齐。例如,一个
int
double
operator new(size_t size, std::align_val_t alignment)
posix_memalign
_aligned_malloc
线程安全是另一个重头戏。在多线程环境中,如果你的自定义分配器没有妥善处理并发访问,就会出现竞争条件,导致内存损坏、崩溃或者数据不一致。这意味着你需要使用互斥锁(
std::mutex
别忘了异常安全。当
operator new
std::bad_alloc
最后,还有数组版本的 new[]
delete[]
new[]
delete[]
new[]
delete[]
delete[]
new[]
delete[]
处理这些陷阱,需要对C++内存模型、底层操作系统内存管理以及并发编程有扎实的理解。这是一个不断学习和迭代的过程。
既然我们谈到了自定义内存管理器,那就不可能不提块分配器(Block Allocator)。在我看来,它是一个非常实用且相对容易上手的自定义内存管理方案,尤其适用于那些需要频繁分配和释放固定大小对象的场景。比如,游戏中的粒子、AI寻路节点、网络消息包等,它们通常大小固定且生命周期短暂。
一个简单的块分配器核心思想是:
当请求分配内存时:
std::bad_alloc
当请求释放内存时:
下面是一个概念性的代码结构,展示了如何实现一个非常简陋的固定大小块分配器。它省略了线程安全、错误检查和更复杂的内存池管理(比如多个内存池、动态扩展),但足以说明其工作原理。
#include <cstddef> // for size_t
#include <iostream>
// 假设我们只分配固定大小的块,比如128字节
const size_t BLOCK_SIZE = 128;
const size_t NUM_BLOCKS = 100; // 预分配100个块
// 定义一个简单的空闲块结构,它会覆盖掉实际的数据
// 当一个块是空闲的时候,它的开头被用作指向下一个空闲块的指针
struct FreeBlock {
FreeBlock* next;
};
// 我们的块分配器类
class FixedBlockAllocator {
private:
char* _memoryPool; // 预分配的内存池
FreeBlock* _freeList; // 空闲块链表
public:
FixedBlockAllocator() : _memoryPool(nullptr), _freeList(nullptr) {
// 预分配一大块内存
_memoryPool = new char[BLOCK_SIZE * NUM_BLOCKS];
if (!_memoryPool) {
throw std::bad_alloc();
}
// 初始化空闲链表:将所有块串联起来
for (size_t i = 0; i < NUM_BLOCKS; ++i) {
FreeBlock* currentBlock = reinterpret_cast<FreeBlock*>(_memoryPool + i * BLOCK_SIZE);
currentBlock->next = _freeList; // 将当前块指向之前的空闲列表头
_freeList = currentBlock; // 更新空闲列表头为当前块
}
std::cout << "FixedBlockAllocator initialized with " << NUM_BLOCKS << " blocks of " << BLOCK_SIZE << " bytes each." << std::endl;
}
~FixedBlockAllocator() {
delete[] _memoryPool;
_memoryPool = nullptr;
_freeList = nullptr; // 确保指针清空
std::cout << "FixedBlockAllocator destroyed." << std::endl;
}
// 分配一个块
void* allocate(size_t size) {
if (size > BLOCK_SIZE) {
// 如果请求的内存大小超过了我们块的大小,这个分配器无法处理
// 实际中可能退化到调用全局new,或者抛出异常
std::cerr << "Warning: Request size " << size << " exceeds block size " << BLOCK_SIZE << ". Cannot allocate." << std::endl;
return nullptr;
}
if (!_freeList) {
// 没有可用的空闲块了
std::cerr << "Error: FixedBlockAllocator out of memory!" << std::endl;
return nullptr; // 或者抛出 std::bad_alloc
}
FreeBlock* allocatedBlock = _freeList;
_freeList = _freeList->next; // 更新空闲列表头
std::cout << "Allocated a block." << std::endl;
return allocatedBlock;
}
// 释放一个块
void deallocate(void* ptr) {
if (!ptr) return;
// 简单检查ptr是否在我们内存池范围内 (实际需要更严格的检查)
if (ptr < _memoryPool || ptr >= (_memoryPool + BLOCK_SIZE * NUM_BLOCKS)) {
std::cerr << "Warning: Deallocating memory not from this allocator. Ignoring." << std::endl;
return;
}
FreeBlock* freedBlock = reinterpret_cast<FreeBlock*>(ptr);
freedBlock->next = _freeList; // 将释放的块加回空闲列表头
_freeList = freedBlock;
std::cout << "Deallocated a block." << std::endl;
}
};
// 全局的块分配器实例
FixedBlockAllocator g_allocator;
// 重载全局的 new 和 delete,让它们使用我们的块分配器
void* operator new(size_t size) {
return g_allocator.allocate(size);
}
void operator delete(void* p) noexcept {
g_allocator.deallocate(p);
}
// 示例类,大小刚好是128字节 (为了演示方便)
struct MyData {
char data[120]; // 加上VMT和padding,假设总共128字节
int id;
MyData(int _id = 0) : id(_id) { std::cout << "MyData " << id << " constructed." << std::endl; }
~MyData() { std::cout << "MyData " << id << " destructed." << std::endl; }
};
int main() {
std::cout << "--- Using custom fixed block allocator ---" << std::endl;
MyData* obj1 = new MyData(1);
MyData* obj2 = new MyData(2);
MyData* obj3 = new MyData(3);
delete obj1;
delete obj2;
MyData* obj4 = new MyData(4); // 应该复用之前释放的块
// 尝试分配一个过大的对象,会失败
// struct LargeData { char big_data[200]; };
// LargeData* bigObj = new LargeData(); // 这会打印错误信息并返回nullptr
delete obj3;
delete obj4;
// 此时,所有块应该都已回到空闲列表
return 0;
}这个例子展示了块分配器的基本工作模式:预分配、切分、通过空闲列表管理。它的优点在于分配和释放的速度极快,因为它们仅仅是链表头部的操作,几乎是O(1)复杂度,并且完全消除了内存碎片问题(对于固定大小的块而言)。当然,它的缺点也很明显:只能分配特定大小的内存,如果请求大小不匹配,它就无能为力了。
实际的生产级内存管理器会复杂得多,比如会包含多个不同大小的内存池、内存池的动态扩展、线程局部缓存、以及更精细的错误处理和调试工具。但这个基础的块分配器,无疑是理解自定义内存管理一个非常好的切入点。
以上就是如何实现自定义内存管理器 重载new和delete操作符示例的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号