很棒的问题!C++ 智能指针是现代 C++ 的精髓之一。
C++ 智能指针 = 自动管理内存的对象,像指针一样使用,但自动释放资源,不用你手动 delete。
🧠 一句话概括
智能指针类型 | C++ 版本 | 特点 | 推荐使用? |
---|---|---|---|
auto_ptr | C++98 / C++03 | 独占所有权,赋值会转移指针 | ❌ 已弃用 |
shared_ptr | C++11+ | 引用计数共享所有权 | ✅ 推荐 |
✅ 示例对比
🔹 auto_ptr
示例(已弃用❗)
#include <iostream>
#include <memory> // auto_ptr 也在 memory 中using namespace std;int main() {auto_ptr<int> p1(new int(10)); // p1 拥有该内存auto_ptr<int> p2 = p1; // p1 的所有权转移给了 p2,p1 变成空指针!cout << *p2 << endl;// cout << *p1 << endl; // ❌ 崩溃!p1 已不再拥有资源return 0;
}
✨ auto_ptr 特点:
- 所有权会在拷贝构造或赋值时自动转移
- 原来的指针会被置空
- 不能多个指针共享同一资源
- C++11 中已被弃用!
🔹 shared_ptr
示例(现代首选 ✅)
#include <iostream>
#include <memory> // for shared_ptrusing namespace std;int main() {shared_ptr<int> p1 = make_shared<int>(20); // 引用计数 +1shared_ptr<int> p2 = p1; // 引用计数 +1(现在是 2 个 shared_ptr 管一个对象)cout << "p1: " << *p1 << ", p2: " << *p2 << endl;cout << "use_count: " << p1.use_count() << endl; // 输出 2p1.reset(); // 引用计数 -1,但对象还没被释放,因为 p2 还在cout << "After p1.reset(), use_count: " << p2.use_count() << endl;return 0; // 最后 p2 离开作用域,引用计数为 0,内存自动释放
}
✨ shared_ptr 特点:
- 多个指针共享资源
- 自动维护引用计数
- 引用计数为 0 时,自动释放内存
- 安全、推荐用在多个地方持有对象时
🚫 auto_ptr 的危险场景(bug 多)
void pass(auto_ptr<int> p) {// 拿到资源,但调用后原来的指针被清空,副作用巨大!
}int main() {auto_ptr<int> p1(new int(5));pass(p1);cout << *p1 << endl; // ❌ 运行时崩溃!p1 已无效
}
✅ shared_ptr 的正确传参方式
void use(shared_ptr<int> p) {cout << "in use(): " << *p << endl;
}int main() {shared_ptr<int> p1 = make_shared<int>(99);use(p1); // 安全传参,引用计数 +1,函数结束后自动减少cout << "Back in main: " << *p1 << endl;
}
✅ 总结对比表
对比点 | auto_ptr | shared_ptr |
---|---|---|
所有权 | 独占,赋值会转移 | 引用计数,共享所有权 |
拷贝行为 | 所有权转移,原指针失效 | 引用计数 +1,不转移所有权 |
生命周期管理 | 自动释放 | 自动释放(引用为 0) |
安全性 | ⚠️ 容易出 bug | ✅ 安全 |
C++版本支持 | C++98/03,已弃用 | C++11+ |
推荐使用? | ❌ 不建议 | ✅ 推荐 |
如果你还想了解 unique_ptr
(只能有一个拥有者)或者 weak_ptr
(解决 shared_ptr 循环引用问题)我也可以继续讲!要不要顺便来一发?😄
🎯 常用智能指针类型
智能指针 | 所属头文件 | 所有权 | 引用计数 | 特点说明 |
---|---|---|---|---|
unique_ptr | <memory> | 独占 | ❌ | 无法拷贝,只能转移 |
shared_ptr | <memory> | 共享 | ✅ | 引用计数 + 自动释放 |
weak_ptr | <memory> | 弱引用 | 🚫 | 不增加引用计数,避免循环引用 |
🧪 用法示例合集(涵盖所有智能指针)
✅ 1. unique_ptr
基本用法(独占资源)
#include <iostream>
#include <memory>int main() {std::unique_ptr<int> up = std::make_unique<int>(42);std::cout << "*up = " << *up << std::endl;// std::unique_ptr<int> up2 = up; ❌ 错误,不能拷贝std::unique_ptr<int> up2 = std::move(up); // ✅ 所有权转移if (!up) {std::cout << "up 已被转移" << std::endl;}return 0;
}
✅ 2. shared_ptr
基本用法(共享资源)
#include <iostream>
#include <memory>int main() {std::shared_ptr<int> sp1 = std::make_shared<int>(100);std::shared_ptr<int> sp2 = sp1; // 引用计数 +1std::cout << "use_count = " << sp1.use_count() << std::endl; // 输出 2sp1.reset(); // 引用计数 -1std::cout << "use_count = " << sp2.use_count() << std::endl; // 输出 1return 0;
}
✅ 3. weak_ptr
解决 shared_ptr 循环引用问题
#include <iostream>
#include <memory>struct Node {std::shared_ptr<Node> next;std::weak_ptr<Node> prev; // ✅ 避免循环引用
};int main() {auto node1 = std::make_shared<Node>();auto node2 = std::make_shared<Node>();node1->next = node2;node2->prev = node1; // weak_ptr 不会增加引用计数std::cout << "node1 use_count = " << node1.use_count() << std::endl; // 输出 1std::cout << "node2 use_count = " << node2.use_count() << std::endl; // 输出 2return 0;
}
明白了!你是卡在“为什么 a 和 b 离开作用域,内存还释放不了”这一点。这个是 shared_ptr 的循环引用核心问题,我们来从内存角度 + 图解角度彻底解释清楚👇
🧠 你以为这样:
{std::shared_ptr<Node> a = std::make_shared<Node>();std::shared_ptr<Node> b = std::make_shared<Node>();
}
作用域一结束,a 和 b 自动销毁,内存应该释放对吧?✅ 正常情况下确实会释放。
🚨 但如果它们互相引用对方,事情就变了!
struct Node {std::shared_ptr<Node> next;std::shared_ptr<Node> prev; // ❌ 大坑就在这里
};
int main() {std::shared_ptr<Node> a = std::make_shared<Node>();std::shared_ptr<Node> b = std::make_shared<Node>();a->next = b; // b 的引用计数 +1b->prev = a; // a 的引用计数 +1 ❗❗❗return 0; // 看似结束了,其实它们还“互相抓着对方”
}
🔄 实际发生了什么?(引用关系图)
变量 | 被谁引用 | use_count |
---|---|---|
a | main 中的变量 + b->prev(shared) | 2 |
b | main 中的变量 + a->next(shared) | 2 |
❌ 当你执行 return 0;
时
a 和 b 在栈上销毁,main 中的 shared_ptr 被释放
但是!还剩下彼此内部 shared_ptr
指着对方!
所以引用计数各为 1,没人能先释放谁,就像两个互相抱住的人,谁也不先放手。
🎯 这就是循环引用!
✅ 4. unique_ptr 管理数组资源
#include <iostream>
#include <memory>int main() {std::unique_ptr<int[]> arr = std::make_unique<int[]>(5);for (int i = 0; i < 5; ++i)arr[i] = i * 10;for (int i = 0; i < 5; ++i)std::cout << arr[i] << " ";std::cout << std::endl;return 0;
}
☑️ 注意:数组要写
int[]
,不能用普通unique_ptr<int>
✅ 5. 自定义 deleter 用法(shared_ptr)
#include <iostream>
#include <memory>void custom_deleter(int* p) {std::cout << "custom deleting " << *p << std::endl;delete p;
}int main() {std::shared_ptr<int> sp(new int(777), custom_deleter); // 指定删除器std::cout << *sp << std::endl;return 0;
}
🧠 最佳实践小贴士
场景 | 推荐用法 |
---|---|
创建新对象 | make_shared<T>() / make_unique<T>() |
自定义资源释放(文件句柄等) | shared_ptr<T>(ptr, deleter) |
避免 shared_ptr 循环引用 | 使用 weak_ptr 弱引用 |
管理数组资源 | unique_ptr<T[]> |
函数参数只读引用 | const std::shared_ptr<T>& |