当前位置: 首页 > ops >正文

【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_ptrstd::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"
http://www.xdnf.cn/news/15151.html

相关文章:

  • Linux操作系统之进程间通信:命名管道
  • 飞算JavaAI:给Java开发装上“智能引擎”的超级助手
  • vue入门学习教程
  • 车载诊断进阶篇 --- 关于网关转发性能引起的思考
  • 匿名函数作递归函数引用
  • uniapp制作一个视频播放页面
  • C++11中的std::minmax与std::minmax_element:原理解析与实战
  • WIFI协议全解析06:Beacon帧、Probe帧你必须懂,搞WiFi通信绕不开它们
  • 【理念●体系】Windows AI 开发环境搭建实录:六层架构的逐步实现与路径治理指南
  • SEQUENCE在RAC多实例开启CACHE的NEXTVAL数值乱序问题
  • 从代码学习深度强化学习 - PPO PyTorch版
  • Go语言WebSocket编程:从零打造实时通信利器
  • Linux操作系统从入门到实战:怎么查看,删除,更新本地的软件镜像源
  • 蔚来测开一面:HashMap从1.7开始到1.8的过程,既然都解决不了并发安全问题,为什么还要进一步解决环形链表的问题?
  • Spring的事务控制——学习历程
  • HarmonyOS NEXT端云一体化开发初体验
  • [Dify] -基础入门4-快速创建你的第一个 Chat 应用
  • 牛客:HJ17 坐标移动[华为机考][字符串]
  • Leaflet面试题及答案(1-20)
  • [实战]调频三角波和锯齿波信号生成(完整C代码)
  • 深入浅出:什么是MCP(模型上下文协议)?
  • 力扣网编程134题:加油站(双指针)
  • C++中柔性数组的现代化替代方案:从内存布局优化到标准演进
  • Debian:从GNOME切换到Xfce
  • 扫描文件 PDF / 图片 纠斜 | 图片去黑边 / 裁剪 / 压缩
  • I2C集成电路总线
  • Semi-Supervised Single-View 3D Reconstruction via Prototype Shape Priors
  • 基于Java Spring Boot开发的旅游景区智能管理系统 计算机毕业设计源码32487
  • linux网络编程之单reactor模型(一)
  • Python 数据建模与分析项目实战预备 Day 2 - 数据构建与字段解析(模拟简历结构化数据)