一:背景

我们知道 C++ 是手工管理内存的分配和释放,对应的操作符就是 new/deletenew[] / 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 的时候,那怎么办呢? 大家应该知道内存自动管理有两种手段。

  1. 引用计数

代表作有 Python,PHP,还有 windows 的句柄管理。

  1. 引用跟踪

代表作有 C#,JAVA 等一众工程化语言。

因为 引用计数 实现比较简单,主要就是记录下对象的引用次数,次数为 0 则释放,所以可完全借助 类的构造函数析构函数栈的自动回收特性 弄一个简单的 引用计数 ,对应着如下四个关键词。

  1. auto_ptr

  2. shared_ptr

  3. unique_ptr

  4. 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. 方式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 会抛异常的,如下图所示:

  1. 方式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 更合理的分权。

最新文章

  1. 千万pv大型web系统架构,学习从点滴开始
  2. Ue4的容器(数据结构)
  3. jquery常用函数与方法
  4. 使用Redux管理你的React应用(转载)
  5. Oracle存储过程获取YYYY-MM-DD的时间格式
  6. ACM: Find MaxXorSum 解题报告-字典树
  7. Qt 之 自定义按钮 在鼠标 悬浮、按下、松开后的效果(全部通过QSS实现)
  8. Mongodb介绍
  9. Tiling
  10. java实现双端链表
  11. 跨域(cross-domain)访问 cookie (读取和设置)
  12. CCIE路由实验(10) -- IS-IS
  13. oracle 日期相减
  14. 「CSS3 」3D效果 &amp; 透视
  15. 各种demo:css实现三角形,css大小梯形,svg使用
  16. oracle dmp数据导入
  17. Python——pip安装报错:is not a supported wheel on this platform
  18. pwn-ROP
  19. ThinkPHP框架 3.2.2 获取系统常量信息 连接数据库 命名空间的理解
  20. web 前端知识体系 网站资源分析

热门文章

  1. 菜B的初来乍到。
  2. clientWidth、offsetWidth、scrollWidth……
  3. PHP反序列化链分析
  4. Django基础之Form和ModelForm组件
  5. 双webview模式,子窗口打不开或者无法切换
  6. [USACO2021DEC] HILO 踩标做法
  7. docker 快速上手
  8. React BrowserHistory 踩坑实录 布置到服务器Nginx上各种静态文件、二级地址404
  9. MySQL - 数据库设计步骤
  10. JAVA - 序列化的方式