【知识讲解】 让指针拥有“自我意识”?--C++智能指针相关内容讲解


目录

前言

[Part1. 自定义删除器](#Part1. 自定义删除器)

[Part1.1. shared_ptr 删除器](#Part1.1. shared_ptr 删除器)

[Part1.2. unique_ptr 删除器](#Part1.2. unique_ptr 删除器)

[Part1.3. 选择建议](#Part1.3. 选择建议)

[Part2. 智能指针问题及解决](#Part2. 智能指针问题及解决)

[Part2.1. 隐式转换的危险](#Part2.1. 隐式转换的危险)

[Part2.2. explicit](#Part2.2. explicit)

[Part2.3. operator bool](#Part2.3. operator bool)

[Part3. weak_ptr 弱指针](#Part3. weak_ptr 弱指针)

[Part3.1. 循环引用问题](#Part3.1. 循环引用问题)

[Part3.2. weak_ptr设计定位](#Part3.2. weak_ptr设计定位)

[Part3.3. weak_ptr接口 lock()](#Part3.3. weak_ptr接口 lock())

[Part4. make_shared](#Part4. make_shared)

[Part5. 结语](#Part5. 结语)


前言

在上篇文章我们粗略的提到了智能指针,这个知识点显然不是那么简单就能理解到位的,我们来看看吧。


let's go!!!!!!!!


Part1. 自定义删除器

默认情况下,智能指针析构时调用delete释放资源。
如果管理数组、文件句柄、第三方资源,默认释放逻辑不再适用,需要自定义删除器。


Part1.1. shared_ptr 删除器

语法示例(管理FILE文件指针)

cpp 复制代码
shared_ptr<FILE> sp(fopen("test.txt","r"), [](FILE* f){
    if(f) fclose(f);
});

• 删除器支持:普通函数、函数对象、Lambda表达式;

• 删除器在构造智能指针时传入。


Part1.2. unique_ptr 删除器

区别:unique_ptr删除器属于模板参数,类型需要写在尖括号内。

cpp 复制代码
// 模板第二个参数声明删除器类型
unique_ptr<FILE, decltype([](FILE* f){fclose(f);})> up(fopen("1.txt","r"),
    [](FILE* f){ if(f) fclose(f); });

如果删除器是Lambda,借助decltype获取Lambda类型作为模板参数。


Part1.3. 选择建议

<1> 不需要资源共享、禁止拷贝场景 → 优先unique_ptr,运行开销更小(无引用计数);

<2> 需要多处共同持有资源 → 使用shared_ptr;

<3> shared_ptr会额外分配控制块,存在内存开销,不要滥用。


Part2. 智能指针问题及解决

Part2.1. 隐式转换的危险

cpp 复制代码
shared_ptr<Date> sp = new Date(); // 合法,允许裸指针隐式构造

隐患:
函数参数接收shared_ptr<T>,直接传入裸指针会发生临时对象构造,容易引发意外生命周期问题。


Part2.2. explicit

智能指针单参数构造函数使用explicit修饰,禁止裸指针直接隐式转换。

cpp 复制代码
// 编译报错,禁止隐式转换
shared_ptr<Date> sp = new Date();

// 正确写法:显式构造
shared_ptr<Date> sp(new Date());

同时explicit也作用于 operator bool:
即:++不能发生int a = sp;这类隐式布尔转换,仅允许if(sp)判断是否持有有效资源。++


Part2.3. operator bool

智能指针重载布尔运算符,用于判断当前是否管理有效资源:

cpp 复制代码
if(sp)
{
    // sp有效,持有内存
}
if(!up)
{
    // up为空,没有资源
}

Part3. weak_ptr 弱指针

Part3.1. 循环引用问题

cpp 复制代码
//双向链表结构示例:
struct Node{
    int val;
    shared_ptr<Node> next;
    shared_ptr<Node> prev;
};

shared_ptr<Node> A = make_shared<Node>();
shared_ptr<Node> B = make_shared<Node>();
A->next = B;
B->prev = A;

问题:
A、B互相持有对方shared_ptr,双方引用计数永远无法减至0,内存永久泄漏。


Part3.2. weak_ptr设计定位

weak_ptr是shared_ptr的辅助工具:

<1> 仅观察资源,不增加引用计数;

<2> 不能直接解引用访问资源;

<3> 可以由shared_ptr构造;

<4> 不支持裸指针直接构造。

cpp 复制代码
//改造循环引用:把其中一侧shared_ptr替换为weak_ptr
struct Node{
    int val;
    shared_ptr<Node> next;
    weak_ptr<Node> prev; // 弱指针,不增加计数
};

Part3.3. weak_ptr接口 lock()

cpp 复制代码
weak_ptr<Node> wp = A->prev;
// lock():尝试提升为shared_ptr,原子增加引用计数
shared_ptr<Node> sp = wp.lock();

if(sp)
{
    // 资源还存在,可以安全使用
}
else
{
    // 原资源已经释放,sp为空
}

原理:
weak_ptr内部依然访问shared_ptr的控制块(引用计数)。

即使原始shared_ptr全部析构,控制块不会立刻销毁,weak_ptr仍然可以读取计数,判断资源是否存活。


Part4. make_shared

cpp 复制代码
// 方式1:两次内存分配
shared_ptr<int> sp(new int(10));

// 方式2:单次内存分配,性能更优
auto sp = make_shared<int>(10);

make_shared优势

<1> 只分配一块连续内存:同时存放对象内存 + shared_ptr控制块;

<2> 减少内存碎片、减少一次堆分配开销;

<3> 异常安全,避免内存泄漏。
注意:make_shared无法自定义删除器。如果需要自定义释放逻辑,只能使用shared_ptr<T>(rawptr, deleter)形式。


Part5. 结语

这篇文章我们认识到了智能指针相关知识,接下来,小编还会带来Linux的相关知识,敬请期待~
最后,祝大家可以:春风得意马蹄疾,一日看尽长安花!

最后的最后,要是觉得本文还可以的话,可以点点赞,关注小编一波,谢谢大家!~

相关推荐
I_belong_to_jesus5 小时前
C++关于分支预测的优化1
开发语言·c++·算法
Rabitebla6 小时前
【Linux】进程控制(十二) 自定义 Shell(二):内建命令与进程工作目录管理
linux·c++·笔记·学习·bash
码客-ygluu11 小时前
一个通用游戏数据服务器
c++·架构
another heaven12 小时前
【C/C++ Ranges 是零开销抽象吗?拆开管道、惰性求值和编译期膨胀】
c语言·开发语言·c++
YLCHUP14 小时前
【思维&图论建模】题解:债权(debt)_图论_建模_思路转化_思维题目_C++算法竞赛
c++·笔记·算法·数学建模·贪心算法·深度优先·图论
xiaoye-duck14 小时前
《C++ 项目:手写大模型接入 SDK》会话模块整合:内存 SessionManager 与 DataManager 持久化联动
开发语言·数据库·c++·语言模型
思麟呀15 小时前
背包DP(01背包+完全背包)
c++·算法
旖旎夜光16 小时前
【C++ 基础入门】C++11 新特性(中):右值引用、移动语义与完美转发
开发语言·c++·笔记·学习
ShineWinsu16 小时前
对于Qt:QTabWidget常见属性的解析
开发语言·数据库·c++·qt·浏览器·qtabwidget·标签页
俊俊几年的18 小时前
动态规划(Dynamic Programming)详解:从入门到 LeetCode Hot 100
linux·c++·算法·leetcode·嵌入式·动态规划·c