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

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. mapreduce (二) MapReduce实现倒排索引(一) combiner是把同一个机器上的多个map的结果先聚合一次

    1 思路:0.txt MapReduce is simple1.txt MapReduce is powerfull is simple2.txt Hello MapReduce bye MapRed ...

  2. KEIL C编译器常见警告与错误信息的解决办法

    对于函数的自变量.局部变量和全局变量声明如果没有指定内存类型,则内存模式将成为内定的内存类型.如果指定了内存类型的变量,则不理会内存模式,完全有所指定的内存类型为主.    SMALL模式:小模式   ...

  3. MyEclipse10 中增加svn插件

    http://www.cnblogs.com/bluesky4485/archive/2012/04/23/2467177.html 确实这种方法可行!向牛人学习!

  4. Centos6.5 qt 安装

    1,centos linux系统必须预先以安装x Server(KDE or GNOME) 2,wget http://download.qt-project.org/official_release ...

  5. BZOJ1754: [Usaco2005 qua]Bull Math

    1754: [Usaco2005 qua]Bull Math Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 374  Solved: 227[Submit ...

  6. SVN项目库错误Unsupported FS format svn: Expected FS format between '1' and '4'; found format '6'

    SVN项目库错误Unsupported FS format svn: Expected FS format between '1' and '4'; found format '6' 从这里找到解决方 ...

  7. DLL入门浅析(4)——从DLL中导出类

    转载自:http://www.cppblog.com/suiaiguo/archive/2009/07/20/90663.html 前面介绍了怎么从DLL中导出函数和变量,实际上导出类的方法也是大同小 ...

  8. 你不知道的关于计算机大师 Dijkstra 的事情

    Dijkstra 的全名叫 Edsger Wybe Dijkstra(艾兹赫尔·韦伯·戴克斯特拉).大部分中国程序员如果能记住这个名字是因为学过计算最短路径的「Dijkstra 算法」,然而大部分人都 ...

  9. Java Socket 编程指南

    Socket,又称为套接字,Socket是计算机网络通信的基本的技术之一.如今大多数基于网络的软件,如浏览器,即时通讯工具甚至是P2P下载都是基于Socket实现的.本文会介绍一下基于TCP/IP的S ...

  10. stm32开发笔记一:使用固件库在RealView-MDK中新建工程(上)

    很久没有碰单片机了,两年了吧,因为项目需要,最近入手一块红牛的开发板,核心为STM32F103ZE.虽然以前做过大概半年的stm32的开发,现在天天在.net平台下写代码,已经忘记的差不多,恰逢周末, ...