在 C++ 现代编程中,智能指针(std::unique_ptr、std::shared_ptr、std::weak_ptr)已经成为管理资源的标准手段。它们通过 RAII 原则自动释放内存,防止内存泄漏和悬空指针。本文将从所有权模型的角度,系统解析三种智能指针的特性与使用场景,并给出实用示例。
一、所有权模型概述
-
独占所有权(
unique_ptr)- 每个对象只能被一个
unique_ptr持有。 - 通过
std::move转移所有权,不能拷贝。
- 每个对象只能被一个
-
共享所有权(
shared_ptr)- 多个指针共享同一资源。
- 内部引用计数,当计数为 0 时自动销毁资源。
-
弱引用(
weak_ptr)- 用来观察共享资源,但不增加引用计数。
- 防止循环引用导致的内存泄漏。
二、unique_ptr 的典型使用
#include <memory>
#include <iostream>
struct Resource {
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource released\n"; }
};
std::unique_ptr <Resource> createResource() {
return std::make_unique <Resource>();
}
int main() {
auto ptr = createResource(); // 资源被创建
// 传递所有权
std::unique_ptr <Resource> ptr2 = std::move(ptr);
// ptr 已经为空
return 0; // ptr2 在此处自动释放
}
unique_ptr适合单一所有者的资源,如文件句柄、网络连接、数据库事务等。
三、shared_ptr 与循环引用
#include <memory>
#include <iostream>
struct Node {
std::shared_ptr <Node> next;
~Node() { std::cout << "Node destroyed\n"; }
};
int main() {
auto a = std::make_shared <Node>();
auto b = std::make_shared <Node>();
a->next = b;
b->next = a; // 循环引用,导致两节点无法销毁
}
- 解决方案:将
next改为 `std::weak_ptr `。
struct Node {
std::weak_ptr <Node> next; // 只观察
};
weak_ptr需要先lock()获得shared_ptr才能使用,若资源已销毁返回空。
四、性能与细节
-
堆分配 vs 栈分配
unique_ptr只管理堆对象,但本身可在栈上。
-
自定义删除器
std::unique_ptr<FILE, decltype(&fclose)> file(fopen("log.txt","w"), &fclose);- 允许对非
new分配的资源使用智能指针。
- 允许对非
-
与数组一起使用
std::unique_ptr<int[]> arr(new int[10]); // 指向数组 arr[3] = 42;unique_ptr的operator[]支持数组访问。
五、实践建议
- 只在需要跨作用域持久化资源时才使用
shared_ptr,否则优先选择unique_ptr。 - 维护好
shared_ptr与weak_ptr的配合,避免循环引用。 - 对于第三方资源或自定义释放逻辑,使用自定义删除器确保资源安全。
六、总结
C++ 的智能指针通过封装资源管理,显著减少了手工内存管理错误。理解所有权模型与引用计数机制,能帮助开发者写出更安全、高效、可维护的代码。未来,随着 C++ 标准库的进一步完善,智能指针将继续成为 C++ 开发不可或缺的工具。