[笔记]cpp智能指针
智能指针
来源视频: 独占智能指针unique_ptr使用技巧_哔哩哔哩_bilibili
这里第三点指的是多线程的情况.
即当多个线程共享一个对象的时候, 程序员难以确定何时释放.
独占指针 unique_ptr
示例
上述程序, 只会调用构造函数. 我们没有手动 delete 的话, 不会调用析构函数. 此时, 我们使用独占指针管理指针 p:
这个语句的意思是: 独占指针的管理的对象的类是 AA, 使用 pu1 管理AA类型指针 p.
这样程序就会在结束的时候执行析构函数.
原理: 独占指针对象 pu1在被销毁的时候会执行自身的析构函数, 而在自身的析构函数中会 delete p. 从而调用了 p 所指对象的析构函数.
使用 pu1 的方式和普通指针基本一样:
实际上, 独占指针有三种初始化方法:
一般使用第一种.
感觉好像有了智能指针, 就没必要再用普通指针了0.0
独占指针的类定义
初始化的时候不可以使用赋值运算符将普通指针赋给独占指针(显然, 这个过程存在类型转换).
在拷贝函数后面加了 = delete, 显式告诉编译器不要提供拷贝构造函数. 否则编译器会自动提供. (cpp11新语法)
不可使用赋值拷贝.
禁用拷贝构造函数和赋值函数的原因是, 防止出现多个独占指针管理同一个普通指针的情况. 举个例子, 假如有两个独占指针指向了同一个普通指针, 在程序结束的时候, 两个指针都会执行自身的析构函数, 结果就是连续对该普通指针执行了两次 delete, 产生错误结果.
然而, 这样依旧阻止不了犯错的可能. 因为我们依旧可以用一个裸指针初始化多个独占指针, 从而导致上述问题.
这个例子也告诉我们, 第一种初始化方法更安全.
get()
方法可以返回独占指针所管理的裸指针.
一个小细节: 直接输出 pu1, 输出结果是 p 指向的地址. 输出 &pu1, 才是 pu1 自身的地址. 对的, pu1 本身有它自己的地址, 不要忘了.
在函数调用中, 只能传独占指针的引用给函数, 不能传值, 这是因为独占指针禁用了拷贝构造函数. 另一种方案是传裸指针给函数. 这个方案在技巧中会提.
智能指针不支持指针的运算 (+ - ++ --).
使用技巧
也就是有两种情况能够赋给另一个: 临时变量 和 函数返回值.
可以认为虽然删除了独占指针的赋值函数, 但是编译器为这两种情况做了特别处理.
两个点:
- 将 nullptr 赋给 pu 后将释放 pu 先前管理的对象;
- 此后 pu 的值为 nullptr.
用于应对不同的场景.
像这种需要原始指针, 但又不负责 delete 的函数, 使用 .get() 方法传给它
这种就用 release 方法.
最常见的情况. 直接传 pu 给它即可.
注意, 这里使用的是值传递.
不过我认为这第四种情况出现的可能性还是非常小, 如果出现了大概率是忘记用引用了.
注意语法: AA[] (实际上改成 AA* 应该也没问题). 之后要访问数组内的元素, 跟正常的数组差不多, 用下标运算符即可.
共享指针 shared_ptr
基本用法和独占指针差不多, 区别在于共享指针可以多个管理一个, 独占指针只能一个管理一个. 也因此, 共享指针没有删除拷贝构造函数和赋值函数
可用 p0.use_count()
来获取引用计数.
推荐第二种, 因为 cpp11 就已经提供, 而独占指针在 14 才提供.
共享指针没有 release() 方法, 因为是否释放对原始指针的控制权不由某一个共享指针决定.
但是反过来不行.
其他大部分使用和独占指针没什么区别
智能指针的删除器
在没有自定义删除器的情况下, 默认的删除器就是只执行 delete 操作.
在初始化的时候指定删除器即可.
在自定义删除器中也可以不执行 delete 操作, 当然后果自负了.
这俩没学过. 是仿函数和 lambda 表达式的知识点.
等价的初始化方式
弱智能指针 weak_ptr
一般来说, 用 weak_ptr 访问资源的方式为: 先用 expired 判断是否过期,
然后用 lock 返回共享指针, 再对这个共享指针操作即可.
以上这个使用方法是错误的, 因为在多线程中这个操作并非原子操作, 不安全. 有可能在判断完 expired 的下一刻, 正好就 expired 了, 然后继续执行了后面的语句.
正确的方法为: 直接用 lock 得到共享指针, 将其赋给另一个共享指针, 然后用 if 语句来判断这个共享指针是否为 nullptr, 如果不是就进行操作.