第8章:定制new和delete
一、导读
1.1 本章学习目标
第8章讲解如何定制 C++ 的内存分配机制:自定义 operator new/delete、内存池、对齐等。
本章核心学习目标:
- 理解 operator new/delete 的工作机制
- 掌握自定义内存分配器的实现
- 理解内存池的设计
- 掌握对齐(alignment)的处理
1.2 为什么本章如此重要
默认的内存分配器不一定适合所有场景。游戏引擎、嵌入式系统等需要定制内存管理以获得更好的性能和可控性。
1.3 核心问题引导
operator new 和 malloc 有什么区别?
内存池如何提高分配效率?
如何处理内存对齐?
二、核心概念详解
2.1 operator new 的工作流程
new Widget(42) 的执行步骤:
1. 调用 operator new(sizeof(Widget)) 分配原始内存
2. 在分配的内存上调用 Widget 的构造函数
3. 返回指向对象的指针
delete ptr 的执行步骤:
1. 调用 ptr 指向对象的析构函数
2. 调用 operator delete(ptr) 释放内存
operator new 失败处理:
- 默认抛出 std::bad_alloc
- 可以安装 new_handler: std::set_new_handler(handler)
- 可以指定 nothrow: new (std::nothrow) Widget2.2 自定义 operator new/delete
cpp
class Widget {
public:
// 类级别的 operator new
static void* operator new(std::size_t size) {
std::cout << "Custom new: " << size << " bytes\n";
void* ptr = std::malloc(size);
if (!ptr) throw std::bad_alloc();
return ptr;
}
// 类级别的 operator delete
static void operator delete(void* ptr) noexcept {
std::cout << "Custom delete\n";
std::free(ptr);
}
// C++14: 带大小的 delete
static void operator delete(void* ptr, std::size_t size) noexcept {
std::cout << "Custom delete (sized): " << size << " bytes\n";
std::free(ptr);
}
};
auto w = new Widget(); // 调用自定义 operator new
delete w; // 调用自定义 operator delete2.3 内存池
cpp
template<typename T, std::size_t BlockSize = 4096>
class MemoryPool {
struct Node {
Node* next;
};
union Slot {
Node node;
char data[sizeof(T)];
};
Node* freeList = nullptr;
std::vector<Slot*> blocks;
void allocateBlock() {
std::size_t numSlots = BlockSize / sizeof(Slot);
Slot* newBlock = static_cast<Slot*>(std::malloc(BlockSize));
blocks.push_back(newBlock);
for (std::size_t i = 0; i < numSlots - 1; ++i) {
newBlock[i].node.next = &newBlock[i + 1].node;
}
newBlock[numSlots - 1].node.next = freeList;
freeList = &newBlock[0].node;
}
public:
void* allocate(std::size_t size) {
if (!freeList) allocateBlock();
Node* node = freeList;
freeList = freeList->next;
return node;
}
void deallocate(void* ptr) {
Node* node = static_cast<Node*>(ptr);
node->next = freeList;
freeList = node;
}
~MemoryPool() {
for (auto block : blocks) std::free(block);
}
};
// 使用:
MemoryPool<Widget> pool;
Widget* w = new (pool.allocate(sizeof(Widget))) Widget();
w->~Widget();
pool.deallocate(w);三、实战案例
3.1 案例一:内存池性能对比
cpp
#include <chrono>
void benchmark() {
const int N = 1000000;
// 标准 new/delete
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N; ++i) {
Widget* w = new Widget();
delete w;
}
auto standard_time = std::chrono::high_resolution_clock::now() - start;
// 内存池
MemoryPool<Widget> pool;
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N; ++i) {
Widget* w = static_cast<Widget*>(pool.allocate(sizeof(Widget)));
pool.deallocate(w);
}
auto pool_time = std::chrono::high_resolution_clock::now() - start;
std::cout << "Standard: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(standard_time).count()
<< " ms\n";
std::cout << "Pool: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(pool_time).count()
<< " ms\n";
}
// 内存池通常比标准 new/delete 快 5-10 倍四、本章小结
operator new/delete 是内存分配的底层接口,可以类级别或全局级别定制
内存池通过预分配和复用内存块,大幅提高分配效率
对齐影响内存访问效率,需要正确处理
五、思考与练习
思考题
内存池为什么比 malloc 快?
什么时候应该自定义 operator new?
编程练习
实现一个线程安全的内存池。
实现一个带对齐支持的内存分配器。