最近做项目, 有个地方是外包人员写的, 其中有个函数,大致这样

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为何可以实现跨模块分配和释放内存的更多相关文章

  1. 智能指针shared_ptr的用法

    为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...

  2. c/c++ 智能指针 shared_ptr 和 new结合使用

    智能指针 shared_ptr 和 new结合使用 用make_shared函数初始化shared_ptr是最推荐的,但有的时候还是需要用new关键字来初始化shared_ptr. 一,先来个表格,唠 ...

  3. c/c++ 智能指针 shared_ptr 使用

    智能指针 shared_ptr 使用 上一篇智能指针是啥玩意,介绍了什么是智能指针. 这一篇简单说说如何使用智能指针. 一,智能指针分3类:今天只唠唠shared_ptr shared_ptr uni ...

  4. C++智能指针shared_ptr

    shared_ptr 这里有一个你在标准库中找不到的—引用数智能指针.大部分人都应当有过使用智能指针的经历,并且已经有很多关于引用数的文章.最重要的一个细节是引用数是如何被执行的—插入,意思是说你将引 ...

  5. 标准库中的智能指针shared_ptr

    智能指针的出现是为了能够更加方便的解决动态内存的管理问题.注:曾经记得有本书上说可以通过vector来实现动态分配的内存的自动管理,但是经过试验,在gcc4.8.5下是不行的.这个是容易理解的,vec ...

  6. C++ 智能指针 shared_ptr 分析

    引文: C++对指针的管理提供了两种解决问题的思路: 1.不允许多个对象管理一个指针 2.允许多个对象管理一个指针,但仅当管理这个指针的最后一个对象析构时才调用delete ps:这两种思路的共同点就 ...

  7. STL源码剖析-智能指针shared_ptr源码

    目录一. 引言二. 代码实现 2.1 模拟实现shared_ptr2.2 测试用例三. 潜在问题分析 你可能还需要了解模拟实现C++标准库中的auto_ptr一. 引言与auto_ptr大同小异,sh ...

  8. 智能指针 shared_ptr 解析

    近期正在进行<Effective C++>的第二遍阅读,书里面多个条款涉及到了shared_ptr智能指针,介绍的太分散,学习起来麻烦.写篇blog整理一下. LinJM   @HQU s ...

  9. 智能指针shared_ptr

    // 智能指针会自动释放所指向的对象. // shared_ptr的应用场景是:程序需要在多个对象间共享数据 /* 先从应用场景入手吧,说矿工A发现了一个金矿. * 然后矿工A喊来了矿工B,一起开采, ...

随机推荐

  1. 转:Eclipse Kepler已支持Java 8

    文章来自于:http://www.infoq.com/cn/news/2014/04/eclipse-kepler-support-java8 期待已久的Java 8已于2014年3月19日正式发布, ...

  2. 【HDOJ】1310 Team Rankings

    STL的应用,基本就是模拟题. /* 1410 */ #include <iostream> #include <string> #include <algorithm& ...

  3. 最小费用最大流MCMF zkw费用流

    稀疏图慢死了...但是稠密图效果还是很好的 struct MCMF{ struct tedge{int x,y,cap,w,next;}adj[maxm];int ms,fch[maxn]; int ...

  4. (转载)APC支持php5.4了

    (转载)http://www.neatstudio.com/archives/?article-2061.html 时隔一年多,APC终于又更新了,这次更新最大的就是支持PHP5.4:- Add PH ...

  5. 【模拟】HDU 5752 Sqrt Bo

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5752 题目大意: 定义f(n)=⌊√n⌋,fy(n)=f(fy-1(n)),求y使得fy(n)=1. ...

  6. Jmeter mysql testing遇到的问题

    问题:执行一条语句可以,多条语句则出错,查询jmter日志,告知sql语法错误. 解决办法: 后来发现需要allowmultiqueries参数置为true,而且,只有mysql connector/ ...

  7. JavaScript String 对象实例深入研究

    本文主要介绍并分析JavaScript中String对象的具体用法,以及和String对象相关的方法,方便开发者在JavaScript开发中更好地处理字符串. 1. 介绍 String 对象,对字符串 ...

  8. Hive从概念到安装使用总结

    一.Hive的基本概念 1.1 hive是什么? (1)Hive是建立在hadoop数据仓库基础之上的一个基础架构: (2)相当于hadoop之上的一个客户端,可以用来存储.查询和分析存储在hadoo ...

  9. int.Parse()与int.TryParse()

      int i = -1;bool b = int.TryParse(null, out i);执行完毕后,b等于false,i等于0,而不是等于-1,切记. int i = -1;bool b = ...

  10. windows 编程 之 问题解决笔记

    问题目录: 1.如何隐藏和显示窗口 2.InvalidateRect在连续使用鼠标或光标时暂时不起作用 3.在VC项目里自己添加头文件和cpp文件在编译阶段报错 4.在static 控件里添加子控件或 ...