shared_ptr 图解

一、什么是智能指针?
智能指针 = 自动管理内存的指针
// 传统指针的痛点 int* p = new int(10); // ... 一堆代码 delete p; // 万一忘了写?内存泄漏! // 智能指针的优雅 #include <memory> shared_ptr<int> sp(new int(10)); // 不用写 delete!自动释放!
二、shared_ptr 核心特性
| 特性 | 说明 |
|---|---|
| 共享所有权 | 多个指针可指向同一对象 |
| 引用计数 | 记录有多少指针在共享 |
| 自动释放 | 最后一个指针销毁时,自动删除对象 |
三、创建 shared_ptr 的三种方式
1. 直接 new(不推荐)
shared_ptr<int> sp1(new int(100)); // 单个int shared_ptr<int[]> sp2(new int[10]); // int数组(c++17)
2. make_shared(✅ 推荐)
auto sp3 = make_shared<int>(100); // 单个int,值100
auto sp4 = make_shared<string>("hello"); // string对象
auto sp5 = make_shared<vector<int>>(); // vector容器
3. 拷贝构造
auto sp6 = sp3; // sp6 和 sp3 指向同一对象
四、引用计数:核心机制
auto sp = make_shared<int>(10);
cout << sp.use_count(); // 输出:1(只有sp自己)
auto sp2 = sp; // 复制
cout << sp.use_count(); // 输出:2(sp和sp2共享)
cout << sp2.use_count(); // 输出:2
{
auto sp3 = sp; // 又复制一个
cout << sp.use_count(); // 输出:3
} // sp3 销毁,计数-1
cout << sp.use_count(); // 输出:2
sp2.reset(); // sp2 放弃指向
cout << sp.use_count(); // 输出:1
sp.reset(); // 最后一个也放弃
// 此时计数为0,对象被自动删除
计数变化图解
开始:sp ──→ [数据:10] 计数=1
sp2复制:sp ──→ [数据:10] ←── sp2 计数=2
sp3复制:sp ──→ [数据:10] ←── sp2 计数=3
↑
sp3
sp3销毁:sp ──→ [数据:10] ←── sp2 计数=2
sp2.reset:sp ──→ [数据:10] 计数=1
sp.reset:对象被删除
五、常用操作详解
1. 解引用和成员访问
auto sp = make_shared<int>(10);
*sp = 20; // 修改值
cout << *sp; // 读取值
auto spstr = make_shared<string>("hello");
cout << spstr->size(); // 调用成员函数
(*spstr)[0] = 'h'; // 修改字符
2. 获取原始指针
auto sp = make_shared<int>(10); int* raw = sp.get(); // 获取原始指针 // ⚠️ 警告:raw只是查看,不能删除! // delete raw; // 千万不要! // shared_ptr<int> sp2(raw); // 千万不要!
3. 重置指针
auto sp = make_shared<int>(10); sp.reset(); // 放弃指向,计数-1 // 也可以重置并指向新对象 sp.reset(new int(20)); // 指向新int,原对象计数-1
4. 检查是否为空
auto sp = make_shared<int>(10);
if (sp) { // ✅ 简洁写法
cout << "指针不为空";
}
if (sp.get() != nullptr) { // ⚠️ 繁琐写法
cout << "指针不为空";
}
六、自定义删除器
在需要特殊清理时使用:
// 方式1:普通函数
void deleteint(int* p) {
cout << "自定义删除函数" << endl;
delete p;
}
shared_ptr<int> sp1(new int(10), deleteint);
// 方式2:lambda表达式(推荐)
shared_ptr<int> sp2(new int(10), [](int* p) {
cout << "lambda删除" << endl;
delete p;
});
// 实际应用:管理文件句柄
shared_ptr<file> spfile(fopen("test.txt", "w"),
[](file* f) {
fclose(f);
cout << "文件已关闭" << endl;
});
七、数组支持
// c++17 开始支持数组 shared_ptr<int[]> sparr(new int[100]); // ✅ sparr[0] = 10; // 数组用 [] 访问 // 推荐写法 auto sparr2 = make_shared<int[]>(100); // c++20
八、指向成员的特殊用法
struct person {
string name;
int age;
double score;
};
auto p = make_shared<person>();
// 指向对象的某个成员,但共享整个对象的生命周期
shared_ptr<string> spname(p, &p->name);
shared_ptr<int> spage(p, &p->age);
cout << p.use_count(); // 输出:3(三个指针共享整个person)
// 即使 spname、spage 只指向成员,它们仍然控制整个对象的生命周期
九、常见错误(一定要记住!)
❌ 错误1:手动删除 get() 返回的指针
auto sp = make_shared<int>(10); delete sp.get(); // 灾难!会重复删除导致崩溃
❌ 错误2:用原始指针创建多个 shared_ptr
int* p = new int(10); shared_ptr<int> sp1(p); shared_ptr<int> sp2(p); // 灾难!两个独立管理同一内存 // 程序结束时两次 delete,崩溃!
❌ 错误3:循环引用(高级话题)
struct node {
shared_ptr<node> next;
};
auto a = make_shared<node>();
auto b = make_shared<node>();
a->next = b;
b->next = a; // 互相引用,都无法释放!
// 解决方案:用 weak_ptr
❌ 错误4:忘记数组语法
shared_ptr<int> sp(new int[10]); // 错误!会调用 delete 不是 delete[] shared_ptr<int[]> sp(new int[10]); // 正确!
十、最佳实践总结
✅ 推荐这样做
// 1. 优先用 make_shared
auto sp = make_shared<int>(10);
// 2. 用 auto 简化代码
auto spvec = make_shared<vector<int>>();
// 3. 用 if(sp) 检查空指针
if (sp) { /* 使用 */ }
// 4. 需要特殊清理时用 lambda
auto spfile = shared_ptr<file>(fopen("a.txt", "r"),
[](file* f){ fclose(f); });
❌ 避免这样做
// 1. 滥用 get() int* raw = sp.get(); // 除非必要 // 2. 手动管理引用计数 // 让系统自动处理就好 // 3. 混用原始指针和智能指针
十一、一句话记忆
shared_ptr 就像合租室友:有人搬进来计数+1,有人搬走计数-1,最后一个离开时负责打扫房间(删除对象)。你只需要安心住,不用操心打扫的事!
到此这篇关于c++实现shared_ptr共享指针的示例代码的文章就介绍到这了,更多相关c++ shared_ptr共享指针内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论