智能指针是封装裸指针的类模板,在析构时自动释放所管理的对象,体现 RAII。标准库在头文件 <memory> 中提供 std::unique_ptr(独占所有权)、std::shared_ptr(共享所有权)、std::weak_ptr(弱引用)等。本节在new/delete之后说明独占与共享所有权入门及选用思路,并衔接类与对象中的资源管理。
为何使用智能指针
- 离开作用域或发生异常时自动
delete,降低泄漏风险。 - 表达所有权:谁负责释放、是否多人共享。
- 与 STL 容器、移动语义配合,减少手写规则三/五。
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 已为空
42make_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。下一章进入类与对象:数据成员与成员函数。