C++11 ——智能指针
C++11 智能指针完全指南
一、引言
C++11 标准引入了一套强大的内存管理工具——智能指针,主要包括 unique_ptr、shared_ptr 和 weak_ptr 三种[[1]]。这些智能指针都包含在 <memory> 头文件中,用于管理动态内存,确保资源被正确释放,从根本上防止内存泄漏[[2]]。
二、智能指针概述
智能指针是一种自动管理内存的指针,内部包含真实指针和引用计数,通过引用计数实现自动释放内存[[3]]。在传统的C++编程中,程序员需要手动使用new和delete来管理内存,这容易出错,可能导致内存泄漏或悬空指针。智能指针将这一过程交给编译器自动处理,大大解放了程序员手动释放内存的压力[[4]]。
三、unique_ptr:独占所有权
3.1 基本概念
unique_ptr 是一种独占智能指针,它禁止其他智能指针与其共享同一个对象,从而保证代码的安全性[[5]]。每个 unique_ptr 指针指向的堆内存空间的引用计数都只能为1,一旦该指针放弃对所指堆内存空间的所有权,则该空间会被立即释放回收[[6]]。
3.2 核心特性
- 不可复制:
unique_ptr不支持拷贝构造和拷贝赋值[[7]] - 支持移动:可通过
std::move转移所有权[[8]] - 独占性:确保同一时间只有一个指针拥有对象[[9]]
- 默认释放器:使用
default_delete释放内存[[10]]
3.3 基本用法示例
#include <memory>
#include <iostream>
std::unique_ptr<int> pInt = std::make_unique<int>(5); // C++14引入
std::unique_ptr<int> pInt(new int(5)); // C++11方式
// 尝试复制会报错(编译失败)
// std::unique_ptr<int> pCopy = pInt; // 错误!
// 正确:移动所有权
std::unique_ptr<int> pOwner = std::move(pInt);
3.4 应用场景
- 函数返回无需同时存在于不同位置的对象
- 资源所有权明确,需要严格控制的场景
- 存储于容器中(如
std::vector<std::unique_ptr<T>>)[[11]]
四、shared_ptr:共享所有权
4.1 基本概念
shared_ptr 通过引用计数实现共享所有权,多个 shared_ptr 可共享同一对象,资源在最后一个引用销毁时自动释放[[12]]。
4.2 核心机制
- 引用计数:当有新的
shared_ptr指向同一个对象时,引用计数加1[[13]] - 自动释放:当
shared_ptr离开作用域时,引用计数减1;当计数为0时,释放所管理的内存[[14]] - 支持拷贝和移动:可以进行拷贝构造和移动构造[[15]]
4.3 基本用法示例
#include <memory>
#include <iostream>
// 推荐方式:使用 make_shared
std::shared_ptr<int> sp = std::make_shared<int>(10);
// 传统方式:通过 new 初始化(不推荐)
// std::shared_ptr<int> sp(new int(10));
// 复制增加引用计数
std::shared_ptr<int> sp2 = sp;
// 查看引用计数
std::cout << sp.use_count() << std::endl; // 输出:2
// 获取原始指针(慎用)
int* rawPtr = sp.get();
// reset 释放当前对象
sp.reset();
4.4 make_shared 的优势
make_shared 是常用的辅助函数模板,可以创建被保护的内存对象并返回 shared_ptr 对象[[16]]。与直接 new 相比,make_shared 具有更好的性能:
- 减少内存分配次数(一次性分配对象和引用计数)
- 提高异常安全性
- 代码更简洁
4.5 注意事项
- 避免多个 shared_ptr 管理同一内存:不要将同一个原始指针初始化为多个
shared_ptr,否则会导致重复释放[[17]] - 避免循环引用:两个或多个
shared_ptr相互引用会导致引用计数永远不为0,引发内存泄漏[[18]] - 慎用 get():调用
get()返回原始指针后需谨慎,避免意外释放[[19]] - 管理 this 指针:不要直接用
shared_ptr管理类的 this 指针[[20]]
五、weak_ptr:观察者的选择
5.1 基本概念
weak_ptr 是一种弱引用指针,与 shared_ptr 的强引用不同,它不会增加引用计数[[21]]。weak_ptr 用于观察由 shared_ptr 管理的对象,防止循环引用导致的内存泄漏[[22]]。
5.2 核心特性
- 不增加引用计数:
weak_ptr本身不控制对象生命周期[[23]] - 可检查有效性:通过
expired()方法检查资源是否已被释放[[24]] - 获取 shared_ptr:通过
lock()方法获取指向原始对象的shared_ptr[[25]] - 解决循环引用:将其中一个
shared_ptr替换为weak_ptr可解决循环引用问题[[26]]
5.3 基本用法示例
#include <memory>
#include <iostream>
std::shared_ptr<int> sp = std::make_shared<int>(10);
std::weak_ptr<int> wp = sp; // 创建 weak_ptr
// 检查是否有效
if (!wp.expired()) {
std::shared_ptr<int> sp2 = wp.lock(); // 获取 shared_ptr
if (sp2) {
std::cout << *sp2 << std::endl;
}
}
// 当 sp 被释放后,wp.expired() 会返回 true
sp.reset();
5.4 循环引用问题示例
struct Node {
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev; // 使用 weak_ptr 打破循环
};
// 如果两个 shared_ptr 互相指向,引用计数永不为0,造成内存泄漏
// 解决方案:将反向引用改为 weak_ptr
六、智能指针使用最佳实践
6.1 优先使用智能指针
- 提高代码安全性
- 增强可维护性
- 减少内存泄漏风险[[27]]
6.2 选择合适类型
| 场景 | 推荐类型 | 理由 |
|---|---|---|
| 独占所有权 | unique_ptr |
明确、高效、无额外开销 |
| 共享所有权 | shared_ptr |
自动引用计数管理 |
| 非拥有者观察 | weak_ptr |
避免循环引用 |
6.3 重要注意事项[[28]]
- 不混合使用:避免将智能指针与普通裸指针混用
- 避免重复管理:不要将同一块内存交给多个智能指针管理
- 优先使用工厂函数:优先使用
make_shared/make_unique - 注意线程安全:
shared_ptr的引用计数操作是线程安全的,但对象本身不是
七、总结
C++11 智能指针是现代 C++ 内存管理的核心工具[[29]]:
- unique_ptr 提供独占所有权,确保内存只被释放一次[[30]]
- shared_ptr 提供共享所有权,通过引用计数保证对象存活[[31]]
- weak_ptr 是非拥有者观察者,不能直接解引用,但可通过
lock()方法临时访问共享对象[[32]]
正确使用智能指针能确保动态内存被正确释放,无论程序是否正常结束[[33]]。智能指针在提高代码安全性和可维护性方面发挥着重要作用,是现代 C++ 开发中不可或缺的工具[[34]]。
所有评论(0)