晓说智能指针shared_ptr为何可以实现跨模块分配和释放内存
最近做项目, 有个地方是外包人员写的, 其中有个函数,大致这样
void getInfo(std::shared_ptr<Info>& outInfo); 这个函数是一个dll(链接静态vc库, 使用/MT链接选项)。
我在exe(也是/MT选项)中使用这个函数, 一开始看了,感觉危险,为啥呢?因为我是这样调用的
f()
{
std::shared_ptr<Info> tmpInfo;
getInfo(tmpInfo);
}
所以,内存是在dll里面分配的,但是 内存在f退出时,析构 tmpInfo, 肯定会有释放的动作,这岂不是 dll分配 , exe释放吗?而且还是静态链接, 必然会 堆冲突啊, 然而无论我怎么测试 , 就是好的出奇, 没有任何挂掉的迹象。 我想了好几天, 也跟了好几次std::shared_ptr的代码, 明明最后看到有 delete p这样的类似语句, 那么delete不是释放了不该管的内存吗?
今天又跟踪, 突然发现一个诡异的问题,发现在f退出前, tmpInfo析构时竟然 代码最后进入到了dll里面进行内存释放的调用,大致就是调用了一个函数 _destroy()的。 我跟了几次, 中间停下来想了各种可能的原因, 后来看到了reset时的一个 调用
shared_ptr(px这是那个分配的指针).swap(*this), 就是相当于 shared_ptr(px).swap(tmpInfo), 于是我就观察 ,跟踪, 发现在shared_ptr(px)临时对象析构时, 竟然进入了exe模块, 明明就是dll中reset内部的临时变量, 咋就跑到exe了呢? 后来发现 跳转时, 有个 call eax的地方, eax经过了几次mov的计算, 所以猜测应该是虚函数指针寻址进行调用的过程。
后来想了想, 觉得可能就是在swap时 , 临时的shared_ptr从(*this)中交换到了this对象初始化时所在模块的 delete地址, 所以才正确分配this相关的内存。
结合前几次看源代码的过程, 里面有几个对象定义了虚函数, 尤其是_destroy是虚函数, 而且使用了一个父类指针作为记忆的东西。 果然, 继续去看源码, shared_ptr有个 _Ref_cout_base*的成员变量,就是它记忆了相关分配时的信息。 他有个纯虚函数—_destroy,在 在 调用 reset时, 回调用 reset(px, new _Ref_count(px)), 这个 _Ref_count就是_Ref_cout_base的具体实现的子类,
这个子类又实现了接口_destroy(), 基本就是delete px的动作。 所以, 一切明了啦。 在reset时, 会同时调用一下类似的动作:
m_pRefCount = new _Ref_count(px); 这样m_pRefCount就跟调用shared_ptr的reset 时所在模块一致了, 所以, shared_ptr使用 临时对象和this对象的swap完成 reset函数的功能, 就可以保证, this对象的m_pRefCount对象所在模块就是reset调用时所在模块(我这里是dll中), 所以,当f 函数退出时, 会调用 m_pRefCount的_destroy函数, 而这个函数会通过虚函数表中记忆的m_pRefCount对象被初始化时所在模块中定义的该类的_destroy函数(虽然exe也有这个类的所有函数定义), 找到正确的_destroy地址, 而这个_destroy由于跟getInfo函数都是在dll定义的一个具体实现, 所以, 这个_destroy中所有的函数的直接调用(仅限于是直接调用(非虚函数的,有固定地址的),如果他也调用一个虚函数, 说不定通过虚函数表的寻址,又跑了其他模块去了,正如他自己就是这样跨模块的), 就是调用了dll里面的函数(包括delete), 所以, tmpInfo析构时, 调用tmpInfo.m_pRefCount的_destroy函数(虚的),会根据虚表指针走到dll函数里面真正的_destroy地址, 这样, 他所释放的也就跟他所包含的对象都在一个模块。这个关键还是 虚函数 的作用啦。
想一想, 如果m_pRefCount不是虚函数, 那么, 对象中就不会包含 他的成员函数地址, 就不会包含m_pRefCount地址(虚函数的地址间接存放在对象开头), 那么reset就交换不到外部模块函数地址, 当调用_destroy时, 就是调用了所在模块的_destroy, 就会调用所在模块的delete, 这样释放就会有问题。 所以, 正式因为虚函数的作用, 我们可以在对象本身中存放函数地址(固定的cpu寻函数地址指令就可以找到地址), 就可以记忆delete的地址(虽然delete很奇特的编译器实现的东西,语言上不可操控), 这样才可以保证对象的一致释放~~~~ 好啦, 回去睡觉啦, 还在公司呢, 刚吃个水饺, 欧耶~~~~~~
晓说智能指针shared_ptr为何可以实现跨模块分配和释放内存的更多相关文章
- 智能指针shared_ptr的用法
为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...
- c/c++ 智能指针 shared_ptr 和 new结合使用
智能指针 shared_ptr 和 new结合使用 用make_shared函数初始化shared_ptr是最推荐的,但有的时候还是需要用new关键字来初始化shared_ptr. 一,先来个表格,唠 ...
- c/c++ 智能指针 shared_ptr 使用
智能指针 shared_ptr 使用 上一篇智能指针是啥玩意,介绍了什么是智能指针. 这一篇简单说说如何使用智能指针. 一,智能指针分3类:今天只唠唠shared_ptr shared_ptr uni ...
- C++智能指针shared_ptr
shared_ptr 这里有一个你在标准库中找不到的—引用数智能指针.大部分人都应当有过使用智能指针的经历,并且已经有很多关于引用数的文章.最重要的一个细节是引用数是如何被执行的—插入,意思是说你将引 ...
- 标准库中的智能指针shared_ptr
智能指针的出现是为了能够更加方便的解决动态内存的管理问题.注:曾经记得有本书上说可以通过vector来实现动态分配的内存的自动管理,但是经过试验,在gcc4.8.5下是不行的.这个是容易理解的,vec ...
- C++ 智能指针 shared_ptr 分析
引文: C++对指针的管理提供了两种解决问题的思路: 1.不允许多个对象管理一个指针 2.允许多个对象管理一个指针,但仅当管理这个指针的最后一个对象析构时才调用delete ps:这两种思路的共同点就 ...
- STL源码剖析-智能指针shared_ptr源码
目录一. 引言二. 代码实现 2.1 模拟实现shared_ptr2.2 测试用例三. 潜在问题分析 你可能还需要了解模拟实现C++标准库中的auto_ptr一. 引言与auto_ptr大同小异,sh ...
- 智能指针 shared_ptr 解析
近期正在进行<Effective C++>的第二遍阅读,书里面多个条款涉及到了shared_ptr智能指针,介绍的太分散,学习起来麻烦.写篇blog整理一下. LinJM @HQU s ...
- 智能指针shared_ptr
// 智能指针会自动释放所指向的对象. // shared_ptr的应用场景是:程序需要在多个对象间共享数据 /* 先从应用场景入手吧,说矿工A发现了一个金矿. * 然后矿工A喊来了矿工B,一起开采, ...
随机推荐
- (2015年郑州轻工业学院ACM校赛题) C 数列
在我们做完B题之后就去看C题了, 发现很多人都已经做出来了, 并且一血还是我们学弟拿的, 感觉这题不难, 我们举了几个例子之后发现全是Alice 然后我们就决定意淫一下,试试看! 没想到就A了 - . ...
- 【转】(DT系列二)device tree的书写规范
原文网址:http://www.cnblogs.com/biglucky/p/4057478.html devicetree的书写规范 下面从节点,属性,reg,ranges,中断控制器等几个方面叙述 ...
- MapReduce Shuffle And Sort
引言 MapReduce作出保证:进入每个Reducer的数据行都是有序的(根据数据行的键值进行排序).MapReduce将Mapper的输出进行排序并传递给Reducer作为输入的过程称为Shu ...
- java容器类总结
1.java容器分类图 说明:左图为简化图(其中粗线部分是重点的容器),右图为完整容器分类图 2.容器类接口和抽象容器类 2.1 说明 容器接口是容器 ...
- word页面不对齐,如何解决?
http://blog.163.com/haibianfeng_yr/blog/static/34572620201157105439516/
- R学习日记——分解时间序列(非季节性数据)
分解时间序列,就是将一个时间序列拆分成不同的构成元件.一般序列(非季节性序列)包含一个趋势部分和一个不规则部分(也就是随机部分),而如果是一个季节性序列,除以上两个外,还有季节性部分. 在此,我们 ...
- winPcap_4_获取已安装设备的高级信息
由 pcap_findalldevs_ex() 返回的每一个 pcap_if 结构体,都包含一个 pcap_addr 结构体,这个结构体由如下元素组成: 一个地址列表 一个掩码列表 (each of ...
- 均价 和 最新价格 是啥意思 什么是MACD DIFF DEA 指标?
均价=当前时刻成交的总价格/成交的总量 最新价格=当前时刻的价格 一.平滑异同平均线(Moving Average Convergence Divergence)原理:MACD(Moving Aver ...
- 计算两点距离 ios
//计算两点距离 -(float)distanceBetweenTwoPoint:(CGPoint)point1 point2:(CGPoint)point2 { ) + powf(point1.y ...
- Python标准库:内置函数bytearray([source[, encoding[, errors]]])
返回一个新字节数组.这个数组里的元素是可变的.而且每一个元素的值范围: 0 <= x < 256.能够通过"字节与字节数组操作"章节来查看相关字节数组的内容.以下说明一 ...