转载:http://blog.csdn.net/liuzhi1218/article/details/6993135

循环引用:

引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引用的对象。一个简单的例子如下:

#include<string>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp> class parent;
class children; typedef boost::shared_ptr<parent> parent_ptr;
typedef boost::shared_ptr<children> children_ptr; class parent
{
public:
~parent() { std::cout <<"destroying parent\n"; } public:
children_ptr children;
}; class children
{
public:
~children() { std::cout <<"destroying children\n"; } public:
parent_ptr parent;
}; void test()
{
parent_ptr father(new parent());
children_ptr son(new children); father->children = son;
son->parent = father;
} void main()
{
std::cout<<"begin test...\n";
test();
std::cout<<"end test.\n";
}

  

运行该程序可以看到,即使退出了test函数后,由于parent和children对象互相引用,它们的引用计数都是1,不能自动释放,并且此时这两个对象再无法访问到。这就引起了c++中那臭名昭著的内存泄漏。

一般来讲,解除这种循环引用有下面有三种可行的方法:

  1. 当只剩下最后一个引用的时候需要手动打破循环引用释放对象。
  2. 当parent的生存期超过children的生存期的时候,children改为使用一个普通指针指向parent。
  3. 使用弱引用的智能指针打破这种循环引用。

虽然这三种方法都可行,但方法1和方法2都需要程序员手动控制,麻烦且容易出错。这里主要介绍一下第三种方法和boost中的弱引用的智能指针boost::weak_ptr。

强引用和弱引用

一个强引用当被引用的对象活着的话,这个引用也存在(就是说,当至少有一个强引用,那么这个对象就不能被释放)。boost::share_ptr就是强引用。

相对而言,弱引用当引用的对象活着的时候不一定存在。仅仅是当它存在的时候的一个引用。弱引用并不修改该对象的引用计数,这意味这弱引用它并不对对 象的内存进行管理,在功能上类似于普通指针,然而一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。

boost::weak_ptr

boost::weak_ptr<T>是boost提供的一个弱引用的智能指针,它的声明可以简化如下:

namespace boost {

    template<typename T>class weak_ptr {
public:
template <typename Y>
weak_ptr(const shared_ptr<Y>& r); weak_ptr(const weak_ptr& r); ~weak_ptr(); T* get() const;
bool expired() const;
shared_ptr<T> lock() const;
};
}

  

可以看到,boost::weak_ptr必须从一个boost::share_ptr或另一个boost::weak_ptr转换而来,这也说 明,进行该对象的内存管理的是那个强引用的boost::share_ptr。boost::weak_ptr只是提供了对管理对象的一个访问手段。

boost::weak_ptr除了对所管理对象的基本访问功能(通过get()函数)外,还有两个常用的功能函数:expired()用于检测所管理的对象是否已经释放;lock()用于获取所管理的对象的强引用指针。

通过boost::weak_ptr来打破循环引用

由于弱引用不更改引用计数,类似普通指针,只要把循环引用的一方使用弱引用,即可解除循环引用。对于上面的那个例子来说,只要把children的定义改为如下方式,即可解除循环引用:

class children
{
public:
~children() { std::cout <<"destroying children\n"; } public:
boost::weak_ptr<parent> parent;
};

  

最后值得一提的是,虽然通过弱引用指针可以有效的解除循环引用,但这种方式必须在程序员能预见会出现循环引用的情况下才能使用,也可以是说这个仅仅 是一种编译期的解决方案,如果程序在运行过程中出现了循环引用,还是会造成内存泄漏的。因此,不要认为只要使用了智能指针便能杜绝内存泄漏。毕竟,对于 C++来说,由于没有垃圾回收机制,内存泄漏对每一个程序员来说都是一个非常头痛的问题。

weak_ptr解决shared_ptr环状引用所引起的内存泄漏[转]的更多相关文章

  1. [转] weak_ptr解决shared_ptr环状引用所引起的内存泄漏

    http://blog.csdn.net/liuzhi1218/article/details/6993135 循环引用: 引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引 ...

  2. 讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(中)- IOS不为人知的Bug

    前言: 话说昨晚还是前晚,写了一篇:讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(上) 文章写到最后时,多了很多莫名奇妙的问题!!! 为了解决了这些莫名奇妙的问题,我又战斗了2 ...

  3. 讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(下)- block中任性用self

    前言: 在处理完框架内存泄漏的问题后,见上篇:讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(中)- IOS不为人知的Bug 发现业务代码有一个地方的内存没释放,原因很也简单: ...

  4. 讲述Sagit.Framework解决:双向引用导致的IOS内存泄漏(上)

    前言: 好久没写文章了,最近先是重构IT恋.又重写IT恋中. Sagit框架也不断的更新,调整,现在感觉已完美了了相当的多. 今天不写教程,先简单分享一下技术内容. 1:见Block必有:#defin ...

  5. 解决Android5.0以下Dialog引起的内存泄漏

    最近项目开发中,开发人员和测试人员均反应在android5.0以下手机上LeakCanary频繁监控到内存泄漏,如下图所示,但凡用到Dialog或DialogFragment地方均出现了内存泄漏. 如 ...

  6. .net 循环引用是否会造成内存泄漏

    一直想做这么一个测试,人和手的测试.类型"人"有一个属性"手",需要"手"也可以读取"人"的数据.则"手&qu ...

  7. c++内存泄漏原因及解决办法(智能指针)

    内存泄漏 由于疏忽或错误造成程序未能释放已经不再使用的内存的情况.内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费. 内存泄露的 ...

  8. android中handler使用应该注意的问题(解决由handler引起的OOM内存泄漏)

    最近,在项目过程中频繁的使用handler处理一些ui线程上的操作,以及使用handler的postdealy.然而使用过后却不对handler进行处理,进而产生了内存溢出现象,通过google,发现 ...

  9. Android 内存泄漏分析与解决方法

    在分析Android内存泄漏之前,先了解一下JAVA的一些知识 1. JAVA中的对象的创建 使用new指令生成对象时,堆内存将会为此开辟一份空间存放该对象 垃圾回收器回收非存活的对象,并释放对应的内 ...

随机推荐

  1. Java作业-多线程

    未完成,占位以后补 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 书面作业 本次PTA作业题集多线程 源代码阅读:多线程程序BounceThread 1.1 Ball ...

  2. 关于python中的operator.itemgetter()函数的用法

    1. operator.itemgetter(num)函数 表示对对象的第num维数据进行操作获取. >>>import operator >>>a = [1, 2 ...

  3. 关于tomcat部署应用的三种方式

    关于tomcat部署应用虽然不是一个经常的操作,因为一旦选择了一种部署方式,我们其他的应用就会不经大脑的使用这种既定模式, 如果不使用这种部署方式,但是对于其他的部署方式不是很清楚的话,很容易抓瞎,所 ...

  4. DML数据操作语言之查询(二)

    当我们查询出了N条记录之后 ,我们知道一共是几条记录,或者这些记录某一字段(列值)的最大值,最小值,平均值等,就可以使用聚合函数. 1.聚合函数 聚合函数会将null 排除在外.但是count(*)例 ...

  5. Node.js系列文章:编写自己的命令行界面程序(CLI)

    CLI的全称是Command-line Interface(命令行界面),即在命令行接受用户的键盘输入并作出响应和执行的程序. 在Node.js中,全局安装的包一般都具有命令行界面的功能,例如我们用于 ...

  6. 【学习笔记】windows安装jhipster踏坑记录

    序: 入职新公司第二天了,本来第一天是配置环境来着,配了一下午也没搞成那个jhipster的安装,每次以为应该正常的时候都是不对,yo是yeoman的指令,但是我是使用yarn管理的yeoman 纠结 ...

  7. Extensions in UWP Community Toolkit - Overview

    概述 UWP Community Toolkit  中有一个 Extensions 的集合,它们可以帮助开发者实现很多基础功能,省去自己造轮子的过程,本篇我们先来看一下 Extensions 的功能都 ...

  8. SVN (TortioseSVN) 版本控制之忽略路径(如bin、obj、gen)

    在SVN版本控制时,新手经常会遇到这样的问题: 1.整个项目一起提交时会把bin . gen . .project 一同提交至服务器 2.避免提交编译.本地配置等文件在项目中单独对src.res进行提 ...

  9. jquery实现链接的title快速出现

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  10. logback中批量插入数据库的参考代码

    protected void insertProperties(Map<String, String> mergedMap, Connection connection, long eve ...