(六)boost库之内存管理shared_ptr

1、shared_ptr的基本用法
    boost::shared_ptr<int> sp(new int(10));     //一个指向整数的shared_ptr
    assert(sp.unique());                        //现在shared_ptr是指针的唯一持有者
    boost::shared_ptr<int> sp2 = sp;            //第二个shared_ptr,拷贝构造函数
    assert(sp == sp2 && sp.use_count() == 2);   //两个shared_ptr相等,指向同一个对象,引用计数为2
    *sp2 = 100;                                 //使用解引用操作符修改被指对象
    assert(*sp == 100);                         //另一个shared_ptr也同时被修改
    sp.reset();                                 //停止shared_ptr的使用,引用计数减一
    assert(!sp);                                //sp不再持有任何指针(空指针)
    assert(sp2.use_count() == 1);               //sp2引用计数变为1
    sp.reset(new int(20));                      //sp管理一个新对象
    assert(*sp == 20);

2、应用于标准容器

有两种方式可以将shared_ptr应用于标准容器(或者容器适配器等其他容器)。

一种用法是将容器作为shared_ptr管理的对象,如shared_ptr<list<T> >,使容器可以被安全地共享,用法与普通shared_ptr没有区别,我们不再讨论。

另一种用法是将shared_ptr作为容器的元素,如vector<shared_ptr<T> >,因为shared_ptr支持拷贝语义和比较操作,符合标准容器对元素的要求,所以可以实现在容器中安全地容纳元素的指针而不是拷贝。

标准容器不能容纳auto_ptr,这是C++标准特别规定的(读者永远也不要有这种想法)。标准容器也不能容纳scoped_ptr,因为scoped_ptr不能拷贝和赋值。标准容器可以容纳原始指针,但这就丧失了容器的许多好处,因为标准容器无法自动管理类型为指针的元素,必须编写额外的大量代码来保证指针最终被正确删除,这通常很麻烦很难实现。

存储shared_ptr的容器与存储原始指针的容器功能几乎一样,但shared_ptr为程序员做了指针的管理工作,可以任意使用shared_ptr而不用担心资源泄漏。

#include <boost/make_shared.hpp> 
int main()  
{      
    typedef vector<shared_ptr<int> > vs;    //一个持有shared_ptr的标准容器类型      
    vs v(10);                               //声明一个拥有10个元素的容器,元素被初始化为空指针       
    int i = 0;      
    for (vs::iterator pos = v.begin(); pos != v.end(); ++pos)      
    {          
        (*pos) = make_shared<int>(++i);     //使用工厂函数赋值          
        cout << *(*pos) << ", ";            //输出值      
    }      
    cout << endl;       
    shared_ptr<int> p = v[9];      
    *p = 100;      
    cout << *v[9] << endl;  
} 

这段代码需要注意的是迭代器和operator[]的用法,因为容器内存储的是shared_ptr,我们必须对迭代器pos使用一次解引用操作符*以获得shared_ptr,然后再对shared_ptr使用解引用操作符*才能操作真正的值。*(*pos)也可以直接写成**pos,但前者更清晰,后者很容易让人迷惑。vector的operator[]用法与迭代器类似,也需要使用*获取真正的值。

3、使用助手类enable_shared_from_this

为什么要使用enable_shared_from_this,或许你对这个类感到很迷惑,先看看下面这种情况:

class MyPoint
{
public:
    MyPoint(){std::cout << "MyPoint" << std::endl;}
    ~MyPoint(){std::cout << "~MyPoint" << std::endl;}
    //返回this的函数
    boost::shared_ptr<MyPoint> GetPoint()
    {
        return boost::shared_ptr<MyPoint>(this);   //错误,将返回一个新的引用计数
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    boost::shared_ptr<MyPoint> p1(new MyPoint);
    boost::shared_ptr<MyPoint> p2 = p1->GetPoint();
    std::cout << p1.use_count() << "," << p2.use_count() << std::endl;  //输出引用计数情况
    p1.reset();          //内存将被释放
}

我们得到的答案将是:

MyPoint 
1,1 
~MyPoint

怎么正确的返回this呢,那么就需要借助enable_shared_from_this了,引入enable_shared_from_this的原因是可以实现返回值为指向该类本身的shared_ptr

正确的写法应该是这样的:

class MyPoint : public boost::enable_shared_from_this<MyPoint>
{
public:
    MyPoint(){std::cout << "MyPoint" << std::endl;}
    ~MyPoint(){std::cout << "~MyPoint" << std::endl;}
    //返回this的函数
    boost::shared_ptr<MyPoint> GetPoint()
    {
        return shared_from_this();
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
     boost::shared_ptr<MyPoint> p1(new MyPoint);
     boost::shared_ptr<MyPoint> p2 = p1->GetPoint();
     std::cout << p1.use_count() << "," << p2.use_count() << std::endl;  //输出引用计数情况
     p1.reset();          //内存将被释放
}

4、定制删除器

当你在使用windows API函数进行编程时,你最烦的或许就是怎么保证申请的内核对象是否关闭,考虑一下代码:

    int *p = (int*) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(p) );
    //业务处理
    //......
    HeapFree( GetProcessHeap(), 0, p );

对象不能通过delete来删除,而是一个释放函数,shared_ptr能否胜任呢,答案是肯定的。

    int *p = (int*) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(p) );
    //此处使用了lambda表达式,需要vs2010或更高版本的支持
    boost::shared_ptr<int> ptr(p, [](int *p){HeapFree( GetProcessHeap(), 0, p );});

5、综合应用示例

下面实现一个线程类,在线程运行结束时,能够自行清理自己的内存

#include <set>
#include <Windows.h>
#include <boost/thread.hpp>
class MySelf;
std::set< boost::shared_ptr<MySelf> > myList;
boost::thread *ptrTh;
class MySelf: public boost::enable_shared_from_this<MySelf>
{
public:
    MySelf()
    {
        printf("MySelf\n");
    }
    void StartThread()
    {
        //启动一个线程
        ptrTh = new boost::thread(&MySelf::Run, this);
    }
    void Run()
    {
        //线程任务函数
        Sleep(5000);
        printf("stop thread\n");
        Stop();
    }
    void Stop()
    {
        //删除自己
        myList.erase(shared_from_this());
    }
    ~MySelf()
    {
        printf("~MySelf\n");
    }
};

void TestSharePtr()
{
    boost::shared_ptr<MySelf> ptr1(new MySelf);
    //保存到list中
    myList.insert(ptr1);
    ptr1->StartThread();
}

(六)boost库之内存管理shared_ptr的更多相关文章

  1. Spark(四十六):Spark 内存管理之—OFF_HEAP

    存储级别简介 Spark中RDD提供了多种存储级别,除去使用内存,磁盘等,还有一种是OFF_HEAP,称之为 使用JVM堆外内存 https://github.com/apache/spark/blo ...

  2. C语言的本质(25)——C标准库之内存管理

    程序中需要动态分配一块内存时怎么办呢?我们可以定义一个缓冲区数组,但是这种方法不够灵活,C89要求定义的数组是固定长度的,而程序往往在运行时才知道要动态分配多大的内存,例如: void foo(cha ...

  3. boost之内存管理

    内存管理一直是令C++程序员最头疼的工作,C++继承了C那高效而又灵活的指针,使用起来稍微不小心就会导致内存泄露.野指针.越界访问等访问.虽然C++标准提供了只能指针std::auto_ptr,但是并 ...

  4. Linux企业级开发技术(6)——libevent企业级开发之内存管理

    默认情况下,libevent使用C库的内存管理函数在堆上分配内存.通过提供malloc.realloc和free的替代函数,可以让libevent使用其他的内存管理器.希望libevent使 用一个更 ...

  5. MFC与Qt的内存管理

    最近在做MFC向Qt的移植,在内存管理方面遇到了很头疼的问题,虽然不知道问题到底出在哪,先了解下这两个库的内存管理方式.于是转载两篇关于内存管理的文章. 一. Qt内存管理: 在Qt的程序中经常会看到 ...

  6. 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  7. redis源码解析之内存管理

    zmalloc.h的内容如下: void *zmalloc(size_t size); void *zcalloc(size_t size); void *zrealloc(void *ptr, si ...

  8. 2万字|30张图带你领略glibc内存管理精髓(因为OOM导致了上千万损失)

    前言 大家好,我是雨乐. 5年前,在上家公司的时候,因为进程OOM造成了上千万的损失,当时用了一个月的时间来分析glibc源码,最终将问题彻底解决. 最近在逛知乎的时候,发现不少人有对malloc/f ...

  9. 详解 boost 库智能指针(scoped_ptr<T> 、shared_ptr<T> 、weak_ptr<T> 源码分析)

    一.boost 智能指针 智能指针是利用RAII(Resource Acquisition Is Initialization:资源获取即初始化)来管理资源.关于RAII的讨论可以参考前面的文章.在使 ...

随机推荐

  1. css案例学习之span边框实现的特殊效果

    bottom left bottom right top left top right 配合颜色来使用,实现一些神奇的效果 #menu a span{ height:; width:; /*borde ...

  2. ubuntu centos debina

    文章目录CentOSDebianCentOS与Debian哪个好?Ubuntu买VPS时有CentOS.Debian.Ubuntu三种操作系统可以选择,Linux下哪种系统更好是很多新人都会遇到的问题 ...

  3. Unity 获取服务器时间 HTTP请求方式

    在写每日签到的时候,我居然使用的是本地时间...被项目经理笑哭了...., 如果你在写单机游戏,没有游戏服务器,但又不想使用本地时间,就可以采用下面方法. 方法总结: 1. 使用HTTP请求获取服务器 ...

  4. 使用ASIHttoRequest需要导入的framework

    需要导入如下framework libxml2.2.dylib libz.1.2.5.dylib MobileCoreServices.framework SystemConfiguration.fr ...

  5. Android 四大组件之 Activity

    1 简介 Activity (活动) 即应用程序 显示的 界面.可以通过两种方式 设置显示的内容 1:纯代码方式 2:xml 布局方式 无论哪一种方式,都是通过 setContentView 来设置显 ...

  6. [Python] 发送email的几种方式

    python发送email还是比較简单的,能够通过登录邮件服务来发送,linux下也能够使用调用sendmail命令来发送,还能够使用本地或者是远程的smtp服务来发送邮件,无论是单个,群发,还是抄送 ...

  7. C# 数据的序列化存取

    1,什么是序列化? 序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程.在序列化期间,对象将其当前状态写入到临时或持久性存储区.以后,可以通过从存储区中读取或反序列 ...

  8. 第002篇 深入体验C#项目开发(一)

    网上摘来的简介:        <深入体验C#项目开发>通过10个综合实例的实现过程,详细讲解了C#在实践项目中的综合运用过程.这些项目从作者的学生时代写起,到项目经理结束,一直贯穿于作者 ...

  9. 【TFS】增加组员,以及用户权限分配

    一.创建windows用户. 二.TFS ->组成员资格->双击 项目集合管理员->添加创建的Windows用户(最高权限) 三.设置权限: TFS权限的复杂,其实也不是很复杂,它只 ...

  10. .NET MySQL的参数化查询

    MySqlConnection conn = new MySqlConnection(SqlConnnectString); MySqlCommand cmd = new MySqlCommand(& ...