zoukankan      html  css  js  c++  java
  • boost之内存管理

      内存管理一直是令C++程序员最头疼的工作,C++继承了C那高效而又灵活的指针,使用起来稍微不小心就会导致内存泄露、野指针、越界访问等访问。虽然C++标准提供了只能指针std::auto_ptr,但是并没有解决所有问题。boost的smart_ptr库是对C++98标准的绝佳补充。它提供了六种智能指针,包括scoped_ptr、scoped_array、shared_ptr、shared_array、week_ptr、instrusive_ptr(不建议使用)。现在我们就学习一下这几种智能指针的使用方法和注意事项:

    #include<boost/smart_ptr.hpp>
    #include<boost/smart_ptr/scoped_ptr.hpp>
    #include<boost/make_shared.hpp>
    #include<boost/enable_shared_from_this.hpp>
    #include<string>
    #include<vector>
    #include<cstdio>
    using namespace std;
    using namespace boost;
    struct posix_file
    {
        posix_file(const char* file_name)
        {
            cout<<"open file:"<<file_name<<endl;
        }
        ~posix_file()
        {
            cout<<"close file"<<endl;
        }
    };
    void any_func(void* p)
    {
        cout<<"some operate"<<endl;
    }
    class self_shared : public enable_shared_from_this<self_shared>
    {
    public:
        self_shared(int n):x(n){}
        int x;
        void print()
        {
            cout<<"self_shared:"<<x<<endl;
        }
    };
    void ptr_test()
    {
        /*scoped_ptr*/
        scoped_ptr<string> sp(new string("text"));
        cout<<*sp<<endl;
        cout<<sp->size()<<endl;
        //delete sp; 不需要delete
        //scoped_ptr<string> sp1(sp);  不允许,拷贝构造函数为私有
        //scoped_ptr<string> sp2 = sp; 不允许,赋值函数为私有
        //sp++; //错误,未定义++操作
        scoped_ptr<int> p(new int);
        if(p)  //在bool语境中测试指针是否有效
        {
            *p=100;    //可以向普通指针一样使用解引用操作符*
            cout<<*p<<endl;
        }
        p.reset();   //reset()置空scoped_ptr
        if(p==0)
        {
            cout<<"该指针为空"<<endl;
        }
        if(!p)  //在bool语境中测试,可以用!操作符
        {
            cout<<"该指针为空"<<endl;
        }
        //将在离开作用域是自动析构,从而关闭文件释放资源
        scoped_ptr<posix_file> fp(new posix_file("a.txt"));
        /*auto_ptr 和 scoped_ptr 区别*/
        auto_ptr<int> ap(new int(10));  //一个int的自动指针
        scoped_ptr<int> sp3(ap);  //从 auto_ptr 获得原始指针
        if(ap.get()==0)  //原auto_ptr不再拥有指针
        {
            cout<<"该指针为空"<<endl;
        }
        ap.reset(new int(20));   //auto_ptr 拥有新的指针
        cout<<*ap<<","<<*sp3<<endl;
    
        auto_ptr<int> ap2;
        ap2 = ap; //ap2从ap获得原始指针,发生所有权转移
        if(ap.get()==0) cout<<"该指针为空"<<endl; //ap不再拥有指针
        scoped_ptr<int> sp2;
        //sp2 = sp;    //赋值操作,无法编译通过
    
        /*scoped_array*/
        /*构造函数接受的指针p必须是new[]的结果,而不是new表达式的结果;*/
        /*没有*和->操作符重载,因为scoped_array持有的不是一个普通的指针*/
        /*析构函数使用delete[]释放资源,而不是delete*/
        /*提供operator[]操作符重载,可以向普通数组一样用下标访问元素*/
        /*没用begin()和end()等类似容器的迭代器操作函数*/
        /*不能拷贝不能赋值*/
        scoped_array<int> sa(new int[100]);  //包装动态数组
        sa[0] = 10;   /*调用[]重载函数 scoped_array 不提供数组索引的范围检查*/
        //*(sa+1) = 20; 错误用法
        int* arr = new int[100];  //一个整数的动态数组
        scoped_array<int> sa1(arr); //scoped_array 对象代理原始动态数组
        fill_n(&sa1[0],100,5);   //可以使用标准库算法赋值数据
        sa1[10] = sa1[20] + sa1[30]; //使用起来像普通数组
        /*不建议使用scoped_array*/
    
        /*shared_ptr*/
        /*shared_ptr是一个最像指针的“智能指针”,是boost.smart_ptr库中最有价值,最重要的组成部分*/
        /*shared_ptr与scoped_ptr一样包装了new操作符在堆上分配的动态对象,它实现的是引用计数型的智能指针,可以自由的拷贝和赋值,可以安全的放在标准容器中*/
        shared_ptr<int> spi(new int);     //一个int的shared_ptr
        if(!spi) cout<<"该指针为空"<<endl;  //在bool语境中隐式转换为bool值
        *spi=253;  //使用解引用操作符*
        shared_ptr<string> sps(new string("smart"));
        cout<<"该字符串长度:"<<sps->size()<<endl;  //使用箭头操作符->
        shared_ptr<int> spi1(new int(10));
        if(spi1.unique()) cout<<"是该指针的唯一持有者"<<endl;
        shared_ptr<int> spi2 = spi1;  //调用拷贝构造函数
        //两个shared_ptr相等,指向同一个对象,引用计数为2
        cout<<"spi1:"<<spi1.use_count()<<",spi2:"<<spi2.use_count()<<endl;
        shared_ptr<int> spi3 = spi2;
        cout<<"spi1:"<<spi1.use_count()<<",spi2:"<<spi2.use_count()<<",spi3:"<<spi3.use_count()<<endl;
        *spi3=100;
        cout<<"*spi1="<<*spi1<<",*spi2="<<*spi2<<"*spi3="<<*spi3<<endl;
        spi3.reset();   //停止shared_ptr的使用
        cout<<"spi1:"<<spi1.use_count()<<",spi2:"<<spi2.use_count()<<",spi3:"<<spi3.use_count()<<endl;
        if(!spi3) cout<<"该指针为空"<<endl;
        /*shared_ptr工厂方法*/
        shared_ptr<string> sps1 = make_shared<string>("make_shared");
        shared_ptr<vector<int> > spv = make_shared<vector<int> >(10,2);
        cout<<"spv的大小:"<<spv->size()<<endl;
        cout<<(*spv)[0]<<endl;
        /*shared_ptr应用与标准容器*/
        typedef vector<shared_ptr<int> > vs;
        vs v(10);
        int i=0;
        for(vs::iterator pos = v.begin();pos!=v.end();++pos)
        {
            (*pos) = make_shared<int> (++i);
            cout<<**pos<<endl;  //此处两次解引用,先获得shared_ptr,在获得其内的值
        }
        shared_ptr<int> pp = v[9];
        *pp = 100;
        cout<<*v[9]<<endl;
        shared_ptr<FILE> fp1(fopen("./1.txt","r"),fclose); //带有删除器的shared_ptr,析构的时候调用fclose关闭内部的指针
        /*shared_ptr<void>*/
        shared_ptr<void> vp((void*)0,any_func);  //容纳空指针,定制删除器
    
        /*shared_array*/
        /*shared_array构造函数接受的指针p必须是new[]的结果,而不能是new表达式的结果*/
        /*shared_array提供operator[]操作符重载,可以像普通数组一样访问元素*/
        /*没有*和->操作符重载,因为shared_array持有的不是一个普通指针*/
        /*析构函数使用delete[]释放资源,而不是delete*/
        int* ip = new int[100];
        shared_array<int> sar(ip);
        shared_array<int> sar1 = sar;
        sar[0]=10;  //shared_array不提供数组索引的范围检查
        cout<<"sa[0]="<<sar[0]<<",sar1[0]="<<sar1[0]<<endl;
        /*shared_array可以用shared_ptr<std::vector>或者std::vector<shared_ptr>来代替*/
    
        /*week_ptr*/
        /*weak_ptr是配合shared_ptr而引入的一种智能指针,没有重载operator*和->*/
        /*weak_ptr充当一个观察者,观察指针的引用计数,在构造和析构的时不会引起引用计数的变化*/
    shared_ptr<int> spp(new int(10)); cout<<"该指针的引用计数:"<<spp.use_count()<<endl; weak_ptr<int> wp(spp);          //从shared_ptr创建weak_ptr cout<<"该指针的引用计数:"<<wp.use_count()<<endl; if(!wp.expired())          //判断week_ptr观察的对象是否失效 { shared_ptr<int> spp1 = wp.lock(); //获得一个shared_ptr *spp1 = 100; cout<<"该指针的引用计数:"<<wp.use_count()<<endl; } cout<<"该指针的引用计数:"<<wp.use_count()<<endl; wp.reset(); //if(!wp) cout<<"该指针无效"<<endl; 没有重载!操作符 if(wp.expired()) cout<<"该指针无效"<<endl; if(!wp.lock()) cout<<"获取指针失败"<<endl; /*week_ptr的的一个重要用途就是获得this指针的shared_ptr,使对象自己能够生产shared_ptr管理自己*/ shared_ptr<self_shared> ssp = make_shared<self_shared>(314); ssp->print(); shared_ptr<self_shared> ssp1 = ssp->shared_from_this(); ssp1->x=1000; ssp1->print(); //警告:不能从一个普通的对象(非shared_ptr)使用shared_from_this()获取shared_ptr,例如: //self_shared ss; //shared_ptr<self_shared> ssp3 = ss.shared_from_this(); /*以上语法上没问题,可以编译通过,但在运行时导致shared_ptr析构时企图删除一个栈上分配的对象,发生未定义行为*/ }

       pool库是Boost程序库在内存管理方面提供的另一个有用的工具,它实现了高效的内存池,用于管理内存资源。pool提供了pool、object_pool、singleton_pool、pool_alloc四种形式的内存池。

    struct demo_class
    {
    public:
        int a,b,c;
        demo_class(int x=1,int y=2,int z=3) : a(x),b(y),c(z){}
    };
    typedef singleton_pool<demo_class,sizeof(int)> sp1; //第一个参数仅仅作为标记,第二个参数每次分配的内存块大小
    void pool_test()
    {
        /*boost.pool库基于简单分隔存储思想实现了一个快速紧凑的内存池库,
         * 不仅能够管理大量的对象的分配/释放小对象很有效,而且不需要delete
         * pool库包括四个组成部分:最简单的pool,分配类实例的 object_pool,
         * 单件内存池singleton_pool和可用标准库pool_alloc*/
        pool<> p1(sizeof(int));  //使用默认分配器分配一个可分配的int的内存池 sizeof(int)为一次欲分配的内存块大小
        int * p = (int*)p1.malloc(); //需要把void*转换为需要的类型
        if(p1.is_from(p)) cout<<"是从该pool分配的内存"<<endl;
        p1.free(p);  //释放内存池分配的内存快
        for(int i=0;i<100;i++)  //连续分配大量的内存
        {
            p1.ordered_malloc(10); //该函数分配的同时合并空闲快链表
        }
    
        /*object_pool使用于类实例的内存池,它的功能与pool类似,但在析构时对所有已经分配的内存快调用析构函数*/
        object_pool<demo_class> p2; //对象对象内存池
    
        demo_class* dp = (demo_class*)p1.malloc();
        if(p2.is_from(dp)) cout<<"从该内存池分配的内存"<<endl;
        //p指向的内存未经过初始化
        cout<<dp->a<<","<<dp->b<<","<<dp->c<<endl;
    
        dp = p2.construct(7,8,9);  //构造一个对象,可以传递参数
        cout<<dp->a<<","<<dp->b<<","<<dp->c<<endl;
        object_pool<string> pls;  //定义一个分配string对象的内存池
        for(int i=0;i<10;i++)  //连续分配大量的string对象
        {
            string * ps = pls.construct("hello object_pool");
            cout<<*ps<<endl;
        }
        /*singleton_pool是一个单件内存池,不需要声明singleton_pool实例,直接用::来调用静态成员函数*/
        int * pi1 = (int*) sp1::malloc();
        if(sp1::is_from(pi1)) cout<<"是该内存池分配的内存"<<endl;
        sp1::release_memory();
    
        /*pool_alloc提供了两个可以用于标准容器模板参数的内存分配器,
         * 分别是pool_alloc和fast_pool_allocator,除了有特别的需求,
         * 我们应该总是使用STL实现自带的内存分配器,使用pool_alloc需要经过仔细的测试,以保证它与容器共同工作
         * */
        vector<int,pool_allocator<int> > v;
        v.push_back(10);
        cout<<v.size()<<v[0]<<endl;
    }
  • 相关阅读:
    ElasticSearch「1」本地安裝Elasticsearch 6.0.1 + Elasticsearch-head插件
    HDFS Erasure Coding介绍
    Cassandra VS HBase
    Hadoop入门 【1】 下载源码,构建
    HBase ProcedureV2 分析
    github创建maven项目过程
    ruby, gem install 出现网络错误
    Ketama Consisent Hash
    [转]产品经理 书目录
    [算法]动态规划之最长公共子序列
  • 原文地址:https://www.cnblogs.com/yu-chao/p/3901318.html
Copyright © 2011-2022 走看看