【C/C++】动态内存分配:从 C++98 裸指针到现代策略
C++ 动态内存分配:从 C++98 裸指针到现代策略
1. 为什么深入研究动态内存
1.1 灵活性与生存期控制
静态/栈分配受限于编译时尺寸和作用域,动态内存允许运行时确定大小,可跨函数保留对象。当需要处理用户输入、缓存数据或实现复杂结构(如树、图)时动态分配不可或缺。
1.2 类型安全与 RAII
现代 C++ 强调 RAII(Resource Acquisition Is Initialization),即资源在对象初始化时获取,在析构时释放,避免内存泄漏、异常安全和悬垂指针。
1.3 性能和碎片管理
频繁的分配和释放会导致碎片化与性能问题,了解 allocator、arena 和 PMR 能帮助构建高性能系统(如游戏引擎、嵌入式系统、网络服务)。
2. C++98:裸指针 new/delete
2.1 基本用法
int *p = new int(42);
int *a = new int[100];
delete p;
delete[] a;
new
调用构造函数,失败抛出std::bad_alloc
。- 忘用
delete[]
会引 undefined behavior。 - 异常早起泄露内存、悬垂指针。
2.2 使用 malloc/free
MyClass* m = (MyClass*)malloc(sizeof(MyClass));
new(m) MyClass(); // placement new
...
m->~MyClass();
free(m);
- 不安全、易出错、不调用构造/析构。
- 极少在 C++ 使用,特殊场景才用 placement new。
3. 现代 C++:RAII 与智能指针
3.1 RAII 原理
RAII 是指资源获取与对象生命周期绑定,C++ 智能指针利用此原则在析构时自动释放内存,从而避免泄漏(turn0search10)。
3.2 std::unique_ptr
auto up = std::make_unique<int>(42);
auto arr = std::make_unique<int[]>(100);
- 独占所有权,禁止复制,仅支持移动。
- 出函数即析构,清理自动完成(turn0search16)。
3.3 std::shared_ptr
与 std::weak_ptr
auto sp = std::make_shared<MyClass>(args...);
std::weak_ptr<MyClass> wp = sp;if(auto locked = wp.lock()){// 使用 locked
}
- 引用计数,最后一个析构时释放资源。
weak_ptr
防止循环引用(turn0search20)。
3.4 智能指针推荐
- 默认用
unique_ptr
,轻量且效率高。 - 多处共享则
shared_ptr
,慎防循环引用。 - 弱引用场景对应
weak_ptr
。
4. 容器替代裸数组
4.1 std::vector
std::vector<int> v;
v.reserve(100); // 预分配避免扩容
for(int i=0; i<100; i++) v.push_back(i);
- 自动管理内存、异常安全(commit or rollback)。
- 推荐替代裸指针数组、新增移植性与维护性。
4.2 智能数组 unique_ptr<T[]>
auto arr = std::make_unique<char[]>(n);
- 用于简单数组,不带完善容器接口。
- 缺少自动扩容机制,不适合复杂数据结构。
5. 高级:Allocators 与 PMR
现代 C++ 引入 allocator 和 polymorphic allocator(PMR),提升控制与性能。
5.1 allocator 模型
- C++ 标准容器模板接受 allocator 用于分配策略。
- 自定义 allocator 后,可实现内存池、arena 等高性能策略。
5.2 PMR 系统(C++17+)
std::pmr::memory_resource
为抽象基类,可继承实现定制行为(turn0search7)。- 四种默认资源:
new_delete_resource()
,null_memory_resource()
,monotonic_buffer_resource
,synchronized_pool_resource
,unsynchronized_pool_resource
(turn0search17)。
5.3 monotonic_buffer_resource
- 基于 bump allocator,分配快但不回收,析构时才释放全部资源(turn0search1, turn0search11).
- 适用于生命周期结束统一释放的场景,如一次请求生命周期,避免释放开销。
示例
std::array<std::byte, 4096> buf;
std::pmr::monotonic_buffer_resource pool(buf.data(), buf.size());
std::pmr::vector<std::pmr::string> vs(&pool);vs.emplace_back("hello");
vs.emplace_back("world");
所有内容都分配在 buf
和上游资源,无释放开销。
5.4 性能对比
monotonic_buffer_resource
分配极快,远优于new_delete_resource
(turn0search13).- 但增长无回收,适合一次性分配使用完毕的场景。
synchronized_pool_resource
提供线程安全池,缓解碎片和性能问题。
6. 性能与碎片管理
6.1 裸指针性能比较
C++98 直接用 new/delete 性能差且易碎裂,频繁调用慢、碎片化严重。
6.2 PMR 与 pool 优势
- pool 避免碎片、提升缓存亲和度。
monotonic_buffer_resource
适合短生命周期,效率高(turn0search11).- 使用自定义 allocator 可进一步优化内存布局和线程安全。
7. 实战案例
7.1 缓存请求数据
struct RequestHandler {std::pmr::monotonic_buffer_resource pool;std::pmr::vector<std::pmr::string> messages;RequestHandler(void* buf, size_t sz): pool(buf, sz), messages(&pool) {}void handle(){messages.emplace_back("OK");// 自动回收等候下次请求}
};
7.2 智能指针管理图结构
struct Node { int value; std::vector<std::shared_ptr<Node>> children; };
auto root = std::make_shared<Node>();
root->children.push_back(std::make_shared<Node>());
自动清理节点,防止泄漏。
7.3 自定义 allocator 示例略述
自行实现继承 memory_resource
缓存或限额策略,然后传给 polymorphic_allocator<T>
。
8. 总结与推荐
场景 | 推荐方式 |
---|---|
普通项目 | 容器 + 智能指针(unique_ptr , vector ) |
性能敏感 / 实时应用 | pmr::monotonic_buffer_resource , 自定义 allocator |
共享所有权 | shared_ptr /weak_ptr |
短期请求生命周期 | bump allocator / pool resource |
参考资料
- RAII 与智能指针解读 ([Stack Overflow][1], [bitesizedengineering.com][2], [LinkedIn][3], [Wikipedia][4], [Stack Overflow][5], [badlydrawnrod.github.io][6])
- PMR 与
monotonic_buffer_resource
原理与示例 - allocator 模型与性能分析
[1]: https://stackoverflow.com/questions/106508/what-is-a-smart-pointer-and-when-should-i-use-one?utm_source=chatgpt.com "What is a smart pointer and when should I use one? - Stack Overflow"
[2]: https://www.bitesizedengineering.com/p/raii-and-smart-pointers-smarter-way?utm_source=chatgpt.com "RAII and Smart Pointers - smarter way to work with your memory"
[3]: https://www.linkedin.com/pulse/polymorphic-allocators-c17-rainer-grimm?utm_source=chatgpt.com "Polymorphic Allocators in C++17 - LinkedIn"
[4]: https://en.wikipedia.org/wiki/Smart_pointer?utm_source=chatgpt.com "Smart pointer"
[5]: https://stackoverflow.com/questions/44799321/what-is-the-purpose-and-usage-of-memory-resource?utm_source=chatgpt.com "What is the purpose and usage of `memory_resource`?"
[6]: https://badlydrawnrod.github.io/posts/2021/12/30/monotonic_buffer_resource/?utm_source=chatgpt.com "Bump Allocators in C++ - My Badly Drawn Self"