【C++】智能指针

目录

一、先搞懂:为什么需要智能指针?

二、三种智能指针,用一句话记住

三、unique_ptr:独生子女

什么时候自动退房?

为什么默认用它?

四、shared_ptr:合租房

场景

关键点

怎么创建?

五、weak_ptr:旁观者

场景

那它有什么用?

六、最经典的问题:循环引用

场景

解决:让一边放手

七、一张图记住全部

[八、实战 ------ 代码](#八、实战 —— 代码)

九、总结


一、先搞懂:为什么需要智能指针?

裸指针问题

普通指针就像一把钥匙:

复制代码
Foo* p = new Foo();  // 拿钥匙,开了一间房
// ... 用着用着忘了归还
// 程序结束,房间还占着 → 内存泄漏

或者:

复制代码
Foo* p = new Foo();
delete p;   // 退房
delete p;   // 又退一次 → 程序崩溃(double free)

或者中途出异常:

复制代码
Foo* p = new Foo();
doSomething();  // 这里抛异常了
delete p;       // 永远执行不到 → 泄漏

核心问题:钥匙在你手里,但"什么时候还"全靠你记得,人会忘,异常会跳

智能指针是什么?

智能指针就是带自动退房功能的房卡

你把房卡交给一个"管家对象",管家在自己离开作用域(生命周期结束)时自动退房,不管你是正常走、还是中途出异常,它都会退。

这就是 RAII:资源绑定到对象生命周期上,对象死,资源自动释放

二、三种智能指针,用一句话记住

类型 一句话
unique_ptr 独生子女:这东西只能一个人拥有,别人要就得"过户"
shared_ptr 合租房:多个人一起拥有,最后一个人走才退房
weak_ptr 旁观者:我只看看,不参与拥有,房客走光了我也不拦着

下面开始介绍!

三、unique_ptr:独生子女

场景

你有一本书,只能属于一个人 。你想给朋友,必须彻底放手,不能你俩都拿着。

复制代码
std::unique_ptr<Foo> a(new Foo());  // a 拥有这本书

// std::unique_ptr<Foo> b = a;  // 错!不能两个人同时拥有
std::unique_ptr<Foo> b = std::move(a);  // 对!把所有权"过户"给 b,a 变空

std::move 就是过户。过户后:

  • b 拥有书。

  • a 变成空(nullptr),不能再用了

什么时候自动退房?

复制代码
void func() {
    std::unique_ptr<Foo> p(new Foo());
    // ...
}  // 函数结束,p 析构 → 自动 delete,你什么都不用写

就这么简单:出了大括号,自动释放。

为什么默认用它?

因为它零额外开销 ,和裸指针几乎一样快,但不用你手动 delete。没有共享需求时,首选它

四、shared_ptr:合租房

场景

一个房子,多个人合租。墙上有个计数器:

  • 每来一个室友,计数 +1。

  • 每走一个室友,计数 -1。

  • 计数归 0 时,房子才退租(对象才析构)。

    std::shared_ptr a = std::make_shared(); // 计数 = 1
    {
    std::shared_ptr b = a; // b 也住进来,计数 = 2
    std::cout << a.use_count(); // 打印 2
    } // b 走了,计数 = 1
    // a 还活着,房子还在
    // a 析构时,计数归 0,房子退租

use_count() 就是当前有几个人住。

关键点

  • 拷贝一份 shared_ptr → 计数 +1。

  • 销毁一份 → 计数 -1。

  • 最后一个销毁的人负责退房。

怎么创建?

复制代码
auto p = std::make_shared<Foo>();  // 推荐,一次分配,效率高

别这么写(有坑):

复制代码
Foo* raw = new Foo();
std::shared_ptr<Foo> p1(raw);
std::shared_ptr<Foo> p2(raw);  // 错!两个独立计数器,最后 double delete

记住:同一个裸指针,只能交给一个 shared_ptr 去管

五、weak_ptr:旁观者

场景

你在咖啡店看窗外的人。你看着他们,但你不拥有他们,他们走了你也不负责。

weak_ptr 就是这种"看着但不拥有"的指针。

它不增加计数,所以它不会阻止对象被销毁。

那它有什么用?

它不能直接访问对象,得先"问一句:人还在吗?"

复制代码
std::weak_ptr<Foo> w = a;   // w 只是观察 a,计数不变

if (auto locked = w.lock()) {
    // 人还在,locked 是个临时的 shared_ptr,可以安全用
    locked->doSomething();
} else {
    // 人已经走了
}

lock() 就是确认还活着,并且临时拿到一张房卡。安全

六、最经典的问题:循环引用

这是 weak_ptr 存在的最主要原因,一定要看懂。

场景

两个人,A 拿着 B 的房卡,B 拿着 A 的房卡。

复制代码
struct Node {
    std::shared_ptr<Node> next;
    std::shared_ptr<Node> prev;
};

auto a = std::make_shared<Node>();
auto b = std::make_shared<Node>();

a->next = b;   // a 抓住 b
b->prev = a;   // b 抓住 a

现在:

  • 外面 a、b 这两个变量销毁了。

  • 但 a 对象还被 b->prev 抓着,b 对象还被 a->next 抓着。

  • 计数永远不归 0,谁也不放手,房子永远退不了 → 内存泄漏。

解决:让一边放手

把其中一个改成 weak_ptr:

bash 复制代码
struct Node {
    std::shared_ptr<Node> next;
    std::weak_ptr<Node> prev;   // 弱引用,不抓人
};

现在 b->prev = a 只是"看着" a,不增加计数。外面的 a 销毁时,a 对象就真的被释放了,链子断了,b 也能释放。

记住一句话:双向关系里,一边用 shared_ptr,另一边用 weak_ptr

七、一张图记住全部

bash 复制代码
unique_ptr  →  独占,不能拷贝,只能 move
shared_ptr  →  共享,有计数,归 0 才释放
weak_ptr    →  旁观,不计数,lock() 才能用

选择口诀:

  1. 默认用 unique_ptr。

  2. 确实要共享,才用 shared_ptr。

  3. 只是观察 / 打破循环,用 weak_ptr

八、实战 ------ 代码

cpp 复制代码
#include <iostream>
#include <memory>

struct Foo {
	Foo() {
		std::cout << "开了房间\n";
	}

	~Foo() {
		std::cout << "退了房间\n";
	}
};

int main() {

	std::cout << "场景1:---\n";
	// 1、unique_ptr
	{
		std::unique_ptr<Foo> up(new Foo());
	}
	//打印出,退出房间

	std::cout << "场景2:---\n";
	//2、shared_ptr
	{
		std::shared_ptr<Foo> sp1 = std::make_shared<Foo>();
		std::cout << "计数:" << sp1.use_count() << std::endl;
		{
			std::shared_ptr<Foo> sp2 = sp1;
			std::cout << "计数:" << sp2.use_count() << std::endl;
		}
		std::cout << "计数:" << sp1.use_count() << std::endl;
	}
	//打印出,退出房间

	std::cout << "场景3:---\n";
	//3、weak_ptr
	{
		std::weak_ptr<Foo> wp;
		{
			std::shared_ptr<Foo> sp = std::make_shared<Foo>();
			wp = sp;
			std::cout << "计数:" << wp.use_count() << std::endl;
		}//sp 没了房间也退了
		if (wp.expired()) {
			std::cout << "房间已经不存在了\n";
		}
	}
	return 0;
}

九、总结

unique_ptr:一个人拥有,要给别人就需要std::move,出了大括号自动释放

shared_ptr:多人合租,计数归0才释放,用make_shared创建

weak_ptr:只观察不拥有,用lock()确认还活着。专门解决循环引用

相关推荐
泡海椒3 小时前
JQuick-Excel VALIDATION 实战:用作用范围约束 Excel 导入校验
开发语言·python·excel
xuxigifxfh3 小时前
HJ11 数字颠倒
java·开发语言·华为机考
一只旭宝4 小时前
数据结构与算法复习手册:从写过,到讲清楚
开发语言·数据结构·c++·算法
百度一下吧4 小时前
断网后 WebView 加载单页面应用:页面跳转 JS 加载失败的处理方案
开发语言·javascript·ecmascript
十五年专注C++开发4 小时前
std::filesystem::exists 在有的win10系统抛异常原因分析
c++·文件系统·filesystem
神仙别闹4 小时前
基于C语言开发的捕捉流星小游戏
c语言·开发语言
程序员Sunday4 小时前
Promise.all、allSettled、race、any 怎么选,失败后其他请求会怎样
开发语言·前端·javascript
hetao17338375 小时前
2026-10-07 hetao1733837 的刷题记录
c++·算法
daxiangxm5 小时前
GEO策略:让AI直接引用你的内容
开发语言·团队开发·运维开发·个人开发·数据库开发