new / delete 管理动态内存极易引发异常安全问题。如果在两者之间调用的函数抛出了异常,或者后续逻辑提前 return,控制流会直接跳转跳过 delete,引发绝对的内存泄漏。try-catch 拦截异常,释放内存后再将异常重新抛出。但在多处连续分配内存的场景下,嵌套捕获和释放逻辑会导致代码极度繁琐和臃肿。void Func() {
int* array1 = new int[10];
int* array2 = new int[10]; // 若此处抛异常,array1 将永远无法释放
try {
// ... 可能抛出异常的业务逻辑 ...
} catch(...) {
// 发生异常时,必须手动清理已分配的所有内存,再重新抛出
delete[] array1;
delete[] array2;
throw;
}
delete[] array1;
delete[] array2;
}
operator*、operator->、operator[])。std::make_unique (C++14)template< class T, class... Args > std::unique_ptr<T> make_unique( Args&&... args );
功能说明:完美转发参数给 T 的构造函数并在堆上创建对象,返回管理该对象的 unique_ptr。
auto p2 = p1; 编译直接拦截报错。auto p2 = std::move(p1); 将所有权转移给 p2,转移后 p1 变空。delete。std::make_shared (C++11)template< class T, class... Args > std::shared_ptr<T> make_shared( Args&&... args );
功能说明:构造对象并返回 shared_ptr。强烈推荐替代 new 使用,可实现内存合并分配。
shared_ptr 的核心是引用计数。因为一份资源可能被多个对象共享,所以必须保证这几个对象看到的是同一个计数器。shared_ptr 都会共享同一个计数,哪怕它们管理的是不同的资源。int* _pcount)。构造时伴随资源 new 一个计数器;拷贝时所有对象指向同一个堆上的计数器,并对其 ++;析构时对计数器 --,
当计数器减到 0 时,说明当前是最后一个管理者,此时释放资源和计数器。

shared_ptr 互相持有对方,导致双方引用计数形成闭环,永远无法降为 0,引发内存泄漏。weak_ptr):专门为打破 shared_ptr 循环引用而生的伴生指针。它不具 RAII 特性,不增加引用计数,仅作为资源的“观察者”。std::weak_ptr::lock / std::weak_ptr::expiredstd::shared_ptr<T> lock() const noexcept; bool expired() const noexcept;
expired() 检查观察的资源是否已释放;lock() 用于在资源有效时,临时提升返回一个强引用 shared_ptr 以安全访问数据。_next 和 _prev 若为 shared_ptr,外部强引用释放后,内部互相依赖对方先析构,形成“回旋镖死锁”。weak_ptr。weak_ptr 没有重载 * 和 ->,无法直接操作资源,必须通过 lock() 获取 shared_ptr 保证线程与生命周期安全。
| 特性 | unique_ptr | shared_ptr |
|---|---|---|
| 所有权 | 独占 (Exclusive) | 共享 (Shared) |
| 拷贝行为 | 严格禁止 | 允许(内部引用计数+1) |
| 开销 | 零(等同裸指针) | 存在(维护原子计数器) |
| 适用场景 | 唯一拥有者 (首选) | 必须在多处模块共享同一个对象 |
unique_ptr 就别用 shared_ptr。TcpServer 独占 Epoller 和 Listener,必须采用 unique_ptr。Connection 既保存在 TcpServer 的 map 中,又需要传给其他工作线程/模块,必须采用 shared_ptr。new 的致命漏洞:如 Process(std::shared_ptr<Widget>(new Widget), ComputePriority());。编译器可能先执行 new Widget,然后执行 ComputePriority()。如果此函数抛出异常,控制流中断,未被智能指针接管的 Widget 将发生内存泄漏。make_* 的原子性:Process(std::make_shared<Widget>(), ComputePriority()); 将内存分配与智能指针构造绑定为不可分割的原子过程,完美堵死并发求值漏洞。shared_ptr 底层不仅有对象,还有维护引用计数的控制块 (Control Block)。std::shared_ptr<Widget> p(new Widget); 发生两次堆内存分配(一次对象,一次控制块),增加系统开销且导致内存碎片化,CPU 缓存命中率低。make_shared 只进行单次合并内存分配,开辟一块连续大内存同时存放对象和控制块,零碎片且极大地优化了 CPU Cache 表现。auto p = std::make_unique<Type>(); 避免了类型名称手写两次的冗余。make_* 写死了 delete。当管理 new[]、文件描述符 (FILE*)、网络 Socket 时,必须使用传统智能指针构造并传入仿函数/Lambda 删除器(注:C++ 标准库已特化 unique_ptr<T[]> 和 shared_ptr<T[]> 解决数组问题)。weak_ptr 纠缠:因 make_shared 连续分配对象与控制块,只要还有 weak_ptr 指向控制块,哪怕强引用归零触发了对象析构,整块极大的内存也无法交还操作系统,引发延迟释放。此时必须分开 new。