智能指针是封装裸指针的类模板,在析构时自动释放所管理的对象,体现 RAII。标准库在头文件 <memory> 中提供 std::unique_ptr(独占所有权)、std::shared_ptr(共享所有权)、std::weak_ptr(弱引用)等。本节在new/delete之后说明独占与共享所有权入门及选用思路,并衔接类与对象中的资源管理。

为何使用智能指针

  • 离开作用域或发生异常时自动 delete,降低泄漏风险。
  • 表达所有权:谁负责释放、是否多人共享。
  • 与 STL 容器、移动语义配合,减少手写规则三/五。

unique_ptr:独占所有权

C++ unique_ptr 示例运行
#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> p = std::make_unique<int>(42);
    std::cout << *p << "\n";
    auto q = std::move(p);
    if (!p) {
        std::cout << "p 已为空\n";
    }
    std::cout << *q << "\n";
    return 0;
}

典型输出:

42
p 已为空
42

make_unique(C++14)一次完成分配与构造;unique_ptr 不可拷贝,可移动。离开作用域时 q 自动释放堆上的 int。

unique_ptr 要点

话题说明
获取裸指针p.get() 不转移所有权;勿对同一对象既 delete 又让 unique_ptr 析构
释放所有权p.release() 返回裸指针并置空 unique_ptr
数组std::unique_ptr<int[]> 使用 delete[]
自定义删除器可管理文件句柄等非 delete 资源

shared_ptr:共享所有权

#include <iostream>
#include <memory>

void bump(std::shared_ptr<int> sp) {
    ++(*sp);
}

int main() {
    auto sp = std::make_shared<int>(10);
    std::cout << "use_count: " << sp.use_count() << "\n";
    bump(sp);
    std::cout << *sp << " count: " << sp.use_count() << "\n";
    return 0;
}

典型输出类似:use_count: 1,随后 11 count: 1(函数返回后计数恢复)。内部维护引用计数;最后一个 shared_ptr 销毁时释放对象。优先使用 make_shared 以减少分配次数。

weak_ptr 与循环引用

若两个对象互相持有 shared_ptr,引用计数无法降为零,会造成泄漏。一方可改用 weak_ptr 观察对象而不增加强引用计数,使用前须 lock() 得到 shared_ptr 并判空。图结构与观察者模式常见此组合。

与裸指针的选择

场景建议
工厂返回堆对象、单一所有者std::unique_ptr
多处共享、寿命难以单一管理std::shared_ptr(注意开销与循环引用)
非拥有、仅访问裸指针或 T& / const T&,前提是对象寿命已保证
动态数组std::vector 或 unique_ptr<T[]>

与类、多态配合

#include <memory>

struct Base {
    virtual ~Base() = default;
};

struct Derived : Base {};

std::unique_ptr<Base> make() {
    return std::make_unique<Derived>();
}

通过基类指针管理派生对象时,基类须有虚析构,见析构函数与多态。

本节说明了 unique_ptr、shared_ptr 的基础语义与 RAII。下一章进入类与对象:数据成员与成员函数。