聊聊 C++ 中的几种智能指针 (上)
一:背景
我们知道 C++ 是手工管理内存的分配和释放,对应的操作符就是 new/delete 和 new[] / delete[], 这给了程序员极大的自由度也给了我们极高的门槛,弄不好就得内存泄露,比如下面的代码:
void test() {
int* i = new int(10);
*i = 10;
}
int main() {
test();
}
这段代码因为用了 new 而忘了 delete,导致在 nt heap 上分配的 i 随着栈地址的回收而成了一块孤悬海外的内存占用,所以修正后的代码如下:
void test() {
int* i = new int(10);
*i = 10;
delete i;
}
int main() {
test();
}
但这种写法比较麻烦,智者千虑必有一失,总会有忘记加 delete 的时候,那怎么办呢? 大家应该知道内存自动管理有两种手段。
- 引用计数
代表作有 Python,PHP,还有 windows 的句柄管理。
- 引用跟踪
代表作有 C#,JAVA 等一众工程化语言。
因为 引用计数 实现比较简单,主要就是记录下对象的引用次数,次数为 0 则释放,所以可完全借助 类的构造函数析构函数 和 栈的自动回收特性 弄一个简单的 引用计数 ,对应着如下四个关键词。
auto_ptr
shared_ptr
unique_ptr
weak_ptr
接下来我们逐个聊一聊。
二:关键词解析
1. auto_ptr
这是 C++ 最早出现一个的 简单引用计数法,参考代码如下:
void test() {
auto_ptr<int> ptr = auto_ptr<int>(new int(10));
}
int main() {
test();
}
接下来看下汇编代码:
auto_ptr<int> ptr = auto_ptr<int>(new int(10));
...
00771D26 call std::auto_ptr<int>::auto_ptr<int> (07710FAh)
00771D2B lea ecx,[ebp-0D8h]
00771D31 call std::auto_ptr<int>::~auto_ptr<int> (0771159h)
可以看到,它分别调用了 构造函数 和 析构函数,接下来找下 auto_ptr 这两个函数的源码。
class auto_ptr {
private:
_Ty* _Myptr; // the wrapped object pointer
public:
auto_ptr(auto_ptr_ref<_Ty> _Right) noexcept {
_Ty* _Ptr = _Right._Ref;
_Right._Ref = nullptr; // release old
_Myptr = _Ptr; // reset this
}
~auto_ptr() noexcept {
delete _Myptr;
}
}
源码一看就明白了,在构造函数中,将 new int 的地址塞给了内部的 _Myptr 指针,在析构函数中对 _Myptr 进行 delete ,真好,这样就不用整天担心有没有加 delete 啦。
值得注意的是,现在 C++ 不推荐这个了,而是建议使用新增的:shared_ptr,unique_ptr,weak_ptr, 怎么说呢? auto_ptr 有一个不好处理的问题,就是现实开发中会出现这么个场景,多个 ptr 指向同一个 引用,如下图:
2. auto_ptr 多引用问题
- 方式1:
定义三个 ptr,然后包装同一个 new int 地址,参考代码如下:
void test() {
int* i = new int(10);
auto_ptr<int> ptr1(i);
auto_ptr<int> ptr2(i);
auto_ptr<int> ptr3(i);
}
这种写法有没有问题呢? 肯定有问题啦,还记得 auto_ptr 的析构是 delete 吗? 对同一块内存多次 delete 会抛异常的,如下图所示:
- 方式2:
既然定义三个有问题, 那就用赋值运算符= 让 ptr1,ptr2,ptr3 指向同一个地址是不是就可以啦? 参考代码如下:
void test() {
int* i = new int(10);
auto_ptr<int> ptr1(i);
auto_ptr<int> ptr2 = ptr1;
auto_ptr<int> ptr3 = ptr2;
}
int main() {
test();
}
那这段代码有没有问题呢? 有没有问题得要看 = 运算符是如何重写的,扒一下源码看看。
template <class _Other>
auto_ptr& operator=(auto_ptr<_Other>& _Right) noexcept {
reset(_Right.release());
return *this;
}
_Ty* release() noexcept {
_Ty* _Tmp = _Myptr;
_Myptr = nullptr;
return _Tmp;
}
从源码看有一个很恶心的点,他会将 _Right 下的 _Myptr 设为 nullptr,也就是说此时的 ptr1 报废了,言外之意就是后续再访问 ptr1 会抛 访问违例。
哈哈,C++里面的专业术语叫 控制权转移。
好了,本篇就说这么多吧,下一篇聊聊新增的这些关键词,看看如何将 auto_ptr 更合理的分权。
聊聊 C++ 中的几种智能指针 (上)的更多相关文章
- 聊聊 C++ 中的几种智能指针 (下)
一:背景 上一篇我们聊到了C++ 的 auto_ptr ,有朋友说已经在 C++ 17 中被弃用了,感谢朋友提醒,今天我们来聊一下 C++ 11 中引入的几个智能指针. unique_ptr shar ...
- c++中的四种智能指针
c++中的四种智能指针 写惯了python,golang再来写c++总觉得头大,很大一个原因就是他没有一个GC机制. 不过c++中提供了智能指针,也不是不能用,李姐万岁! auto_ptr, shar ...
- c++ 中的8种智能指针[转]
一.简介 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 ...
- 转载:STL四种智能指针
转载至:https://blog.csdn.net/K346K346/article/details/81478223 STL一共给我们提供了四种智能指针: auto_ptr.unique_ptr.s ...
- stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结
stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结 1. auto_ptrauto_ptr主要是用来解决资源自动释放的问题,比如如下代码:voi ...
- C++中的四个智能指针
只能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象.智能指针定义在memory头文件中. 1. auto_ptr(C++11已经舍弃) 由new expression获得的对象,在au ...
- 聊聊 Linux 中的五种 IO 模型
本文转载自: http://mp.weixin.qq.com/s?__biz=MzAxODI5ODMwOA==&mid=2666538919&idx=1&sn=6013c451 ...
- 到底有多少种智能指针(smart pointer)
最近Qt的blog总结了到底有多少种smart pointer, 下面是一个简要的介绍: 1. QPointer :提供对指针的保护,当一个指针被删除以后,再使用不会造成野指针或者指针溢出.比如 ...
- 聊聊 SpringBoot 中的两种占位符:@*@ 和 ${*}
前言 在 SpringBoot 项目中,我们经常会使用两种占位符(有时候还会混用),它们分别是: @*@ ${*} 如果我们上网搜索「SpringBoot 的占位符 @」,大部分答案会告诉你,Spri ...
随机推荐
- [题解][ARC089D] ColoringBalls
题目大意 有 \(n\) 个白色的小球排成一排,有一个长为 \(k\) 的字符串 \(S\).接下来进行 \(k\) 次操作. 第 \(i\) 个操作,选择一段连续的小球(可以为空),若 \(S\) ...
- 关于IPC和PTH用户权限问题,psexec拒绝访问(Access Denied)的原因
前瞻 关于net use和psexec无法使用本地管理员组用户建立连接的问题 测试环境: win7系统,存在域环境 域名:de1ay 普通域用户: de1ay\de1ay 域管理员用户:de1ay\A ...
- 在vue-cli中安装scss,且可以全局引入scss的步骤
简历魔板__个人简历模板在线生成 在写vue的css样式时,觉得需要css预处理器让自己的css更加简洁.适应性更强.可读性更佳,更易于代码的维护,于是在vue-cli脚手架采用scss.写过的人都知 ...
- Jmeter接口测试流程详解(中科软测认证中心)
1.jmeter简介 Jmeter是由Apache公司开发的java开源项目,所以想要使用它必须基于java环境才可以: Jmeter采用多线程,允许通过多个线程并发取样或通过独立的线程对不同的功能同 ...
- Swift初探01 变量与控制流
Swift初探01 变量与控制流 输出"hello world"是几乎学习所有编程语言的第一课,这是程序员的情怀. 所以我们学习swift的第一步,就是输出一句"Hell ...
- Spring Security之短信登录
实现短信验证码登录 前面实现了 用户名+密码 的登录方式,现在实现一下短信验证码登录. 开发短信验证码接口 短信验证码和图形验证码类似,用户从手机短信得到验证码和从图片得到验证码类似. 校验短信验证码 ...
- CentOS删除编译安装的Python3
编译安装Python3 # 下载 # wget https://www.python.org/ftp/python/3.6.4/Python-3.6.4.tar.xz wget http://mirr ...
- SQL数据库操作总结
1. 如何增加(创建)数据库 语法 CREATE DATABASE database_name 2.如何增加(创建)数据表 语法 CREATE TABLE table_name { column_na ...
- 【原创】渗透神器CoblatStrike实践(1)
渗透神器CoblatStrike实践(1) 前言 正常的渗透测试: 寻找漏洞,利用漏洞,拿到一定的权限 后渗透(CS为代表的): 提升权限,内网渗透,权限维持 工具地址(非官方取到后门多,建议 ...
- 接口测试postman深度挖掘应用①
一.测试接口前需要搞明白的原理: 1.在讲如何使用postman时,我们首先应该要了解网络的请求相应的知识,下面以fiddle进行抓包为例分析: 通过fiddler抓包我们不难发现,客户端也就是用户会 ...