一、核心原则与常见误区

  • 用 RAII 管理一切资源:资源的生命周期绑定到对象生命周期,析构自动释放,天然支持异常安全。
  • 明确所有权:优先使用 std::unique_ptr(独占),必要时再用 std::shared_ptr(共享),观察者用 std::weak_ptr。
  • 优先用 std::make_unique / std::make_shared 创建对象,减少显式 new/delete 与潜在泄漏路径。
  • 接口设计:输入用引用/const 引用,输出用返回值或智能指针,避免把“谁释放”的语义丢给调用者。
  • 容器优先:用 std::vector / std::string 等替代裸指针+手工数组,减少越界与泄漏。
  • 工具护航:开发期开启 AddressSanitizer、Valgrind 等检测工具,持续集成中纳入内存检查。

      C++内存管理终极指南:从 new-delete 到智能指针,90% 的人都用错了

      二、new/delete 的正确打开方式

  • 基本配对:单个对象用 new/delete,数组用 new[]/delete[],混用是未定义行为。
  • 初始化:内置类型建议值初始化(如 new int()),避免未定义值;类类型会调用默认构造。
  • 失败处理:new 失败默认抛出 std::bad_alloc;若使用 new(std::nothrow),需判空。
  • 常见错误:忘记释放、重复释放、释放后继续使用(悬空指针)、把栈对象 delete、在循环中频繁 new 而不释放。
  • 结论:在现代 C++ 中,直接手写 new/delete 的场景应尽量少,仅在底层库、与 C 接口交互或自定义分配器中保留
  •   三、智能指针选型与用法

  • std::unique_ptr:独占所有权、不可拷贝、可移动;作为类成员/返回值/工厂函数返回值首选;数组用 std::unique_ptr,析构自动 delete[]。
  • std::shared_ptr:共享所有权、引用计数;多线程共享对象或缓存/回调等场景使用;优先 std::make_shared 提升性能与异常安全。
  • std::weak_ptr:不增加引用计数,配合 shared_ptr 打破循环引用;访问前用 lock() 获取临时 shared_ptr 判断有效性。
  • 不要将同一块内存交给多个独立智能指针管理;避免把 get() 返回的裸指针再交给其他智能指针或 delete。

      C++内存管理终极指南:从 new-delete 到智能指针,90% 的人都用错了

      四、实战清单与反模式对照

  • 工厂函数:返回 std::unique_ptr,由调用者决定转移或共享;需要共享再 std::move 进 shared_ptr。
  • 容器持有对象:优先 std::vector>;若需共享,再存 shared_ptr。
  • 观察者/缓存:持有 weak_ptr,使用前 lock();避免缓存已销毁对象。
  • 与 C 接口交互:用 unique_ptr + 自定义删除器(如 fclose/free),把释放职责封装进 RAII。
  • 性能敏感:优先 unique_ptr(零额外开销),谨慎使用 shared_ptr(引用计数与原子操作成本)。
  • 反模式速查:
  • 裸指针满天飞、谁拥有说不清;
  • 一个原始指针被多个智能指针接管;
  • 循环引用导致泄漏;
  • 用 delete 释放栈对象或用错 delete/delete[];
  • 循环中 new 不释放;
  • 接口用裸指针输出资源却不说明释放规则。

      C++内存管理终极指南:从 new-delete 到智能指针,90% 的人都用错了

      五、最小可运行示例

      #include #include #include struct Base { virtual ~Base() { std::cout << "Base destroyed\n"; } virtual void say() const { std::cout << "I am Base\n"; }};struct Derived : Base { ~Derived() override { std::cout << "Derived destroyed\n"; } void say() const override { std::cout << "I am Derived\n"; }};// 工厂:返回独占所有权std::unique_ptr make() { return std::make_unique();}// 共享缓存:用 weak_ptr 观察,避免循环引用class Cache { std::weak_ptr weak_;public: void set(std::shared_ptr p) { weak_ = p; } void use() { if (auto sp = weak_.lock()) sp->say(); else std::cout << "Expired\n"; }};int main() { // 1) 工厂 + 独占 auto p = make(); p->say(); // 2) 共享 + 观察 std::shared_ptr shared = std::move(p); // 转移所有权到 shared Cache cache; cache.set(shared); cache.use(); // 3) 容器持有独占对象 std::vector> vec; vec.push_back(std::make_unique()); vec.push_back(std::make_unique());} // 自动析构,无泄漏

  • 要点:工厂返回 unique_ptr;需要共享再转 shared_ptr;缓存用 weak_ptr 观察;容器持有 unique_ptr 更安全