boost之内存管理

追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

导读:本篇文章讲解 boost之内存管理,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

smart_ptr库概述

计算机系统中资源有很多种,内存是我们最常用到的,此外还有文件描述符、socket、操作系统handle、数据库连接等,程序里申请这些资源后必须及时归还系统,否则就会产生难以预料的后果。

RALL机制(Resource AcquisItion Is Initalization )

为了管理内存等资源,c++程序员通常采用RAT工机制(资源获取即初始化,ResourceAcquisition Is Initialization),在类的构造函数里申请资源,然后使用,最终在析构函数中释放资源。

如果对象是用声明的方式在栈上创建的(一个局部对象),那么RAI工 机制会工作正常,当离开作用域时对象会自动销毁从而调用析构函数释放资源。但如果对象是用new操作符在堆上创建的,那么它的析构函数不会自动调用,程序员必须明确地用对应的delete操作符销毁它才能释放资源。这就存在着资源泄漏的隐患,因为这时没有任何对象对已经获取的资源负责,如果因某些意外导致程序未能执行delete语句,那么内存等资源就永久地丢失了。(参考这一篇)

智能指针

boost.smart_ptr库提供了六种智能指针: scoped_ptr、scoped_array、sharedptr、shared_array、weak_ptr和intrusive_ptr。它们是轻量级的对象,速度与原始指针相差无几,都是异常安全的(exception safe),而且对于所指向的类型T也仅有一个很小且很合理的要求:类型T的析构函数不能抛出异常。

位于:

#include<boost/smart_ptr.hpp>

using namespacce boost;

scoped_ptr

scoped_ptr是一个很类似auto_ptr/unique_ptr的智能指针,它包装了new操作符在堆上分配的动态对象,能够保证动态创建的对象在任何时候都可以被正确地删除。但scoped_ptr的所有权更加严格,不能转让,一旦 scoped_ptr获取了对象的管理权,我们就无法再从它那里取回来

类摘要:

template<typename T>
class scoped_ptr{
private:
T* p;
scoped_ptr(scoped_ptr const & );//拷贝私有化
scoped_ptr&=(scoped_ptr const &);//赋值私有化
void operator==(scoped_ptr const&)const;
void operator!=(scoped_ptr const&)const;
public:
void reset(T*p=0);
T&       operator*()const ;
T&       operator->()const;
T*       get()const;            //获取原始指针
explicit operator bool() const;
void      swap(scoped_ptr &b);

 };
 template<typename T>inline
bool operator==(scoped_ptr<T>const &p,boost::detail::sp_nullptr_t)

操作函数

scoped_ptr的构造函数接受一个类型为T*的指针p,创建出一个scoped ptr对象,并在内部保存指针参数 p。p 必须是一个new表达式动态分配的结果,或者是个空指针(nullptr)。当scoped_ptr对象的生命期结束时,析构函数会使用delete操作符自动销毁所保存的指针对象,从而正确地回收资源。

scoped_ptr同时把拷贝构造函数和赋值操作符都声明为私有的,禁止对智能指针的拷贝操作,保证了被它管理的指针不能被转让所有权。

scoped_ptr用operator* ()和 operator->()重载了解引用操作符“*”和箭头操作符“->”,以模仿被代理的原始指针的行为,因此可以把 scoped_ptr 对象如同指针一样使用。如果scoped ptr保存的是空指针,那么这两个操作的行为未定义。

成员函数get ()返回scoped_ptr内部保存的原始指针,可以用在某些要求必须是原始指针的场景(如底层的c接口)。但使用时必须小心,这将使原始指针脱离scoped ptr的控制!不能对这个指针做delete操作,否则scoped_ptr析构时会对已经删除的指针再进行删除操作,发生未定义行为(通常是程序崩溃,这可能是最好的结果,因为它说明你的程序存在bug)。

注意:reset():重置指针 违背scoped_ptr 意图,不做深入探究

实例:

#include<boost/smart_ptr.hpp>
#include<string>
using namespace boost;

int main()
{
    scoped_ptr<std::string>sp(new std::string("text"));
    assert(sp);
    assert(sp!=nullptr);
    std::cout<<*sp<<std::endl;
    std::cout<<sp->size()<<std::endl;
}
//
text
4

错误实例

scoped_ptr<string> sp2=sp;
//scoped_ptr 不能拷贝构造

class ptr_owned final
{
scoped_ptr<int>m_ptr;
};

ptr_owned p;
ptr_owned p2(p);
scoped_ptr 不能拷贝构造

share_prt(简单介绍)

shared_ptr与scoped_ptr一样包装了new操作符在堆上分配的动态对象,但它实现的是引用计数型的智能指针R,可以被自由地拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它时才删除被包装的动态分配的对象。

类摘要:

template<typename T>
class shared_ptr
{
 public:
 shared_ptr();
 template<typename Y>explicit shared_ptr(Y* p);
 template<typename Y,typename D>shared_ptr(Y*p,D d);
 ~shared_ptr();
 shared_ptr(shared_ptr const & r);
 shared_ptr &operator==(shared_ptr const& r);
 template<class Y>shared_ptr&operator=(shared_ptr<Y>const &r);
 .....
};

操作函数

shared_ptr与 scoped_ptr同样是用于管理new动态分配对象的智能指针,因此功能上有很多相似之处:它们都重载了“*”和“->”操作符以模仿原始指针的行为,提供显式bool类型转换以判断指针的有效性,get ()可以得到原始指针,并且没有提供指针算术操作,也不能管理new[]产生的动态数组指针。

构造函数:

(1)无参的shared_ ptr()创建一个持有空指针的shared ptr;

(2)shared_ptr(Y * p)获得指向类型T的指针p的管理权,同时引用计数置为1。这个构造函数要求Y类型必须能够转换为T类型;

(3)shared_ptr(shared_ptr const & r)从另外一个shared_ptr获得指针的管理权,同时引用计数加1,结果是两个shared_ptr共享一个指针的管理权;

(4)operator=赋值操作符可以从另外一个shared_ptr获得指针的管理权,其行为同拷贝函数

shared_ptr的reset ( )函数的行为与scoped_ptr也不尽相同,它的作用是将引用计数减1,停止对指针的共享,除非引用计数为0,否则不会发生删除操作。带参数的reset ()则类似相同形式的构造函数,原指针引用计数减1的同时改为管理另一个指针。

实例:

#include<boost/smart_ptr.hpp>
#include<string>
using namespace boost;
using namespace std;
class shared
{
    private:
    boost::shared_ptr<int>p;
    public:
    shared(boost::shared_ptr<int>p_):p(p_){}
    void print()
    {
        cout<<"count:"<<p.use_count()<<
        "v="<<*p<<endl;
    }
    
    
};
 void print_funv(boost::shared_ptr<int>p)
    {
        cout<<"count:"<<p.use_count()<<
        "v="<<*p<<endl;
    }

void simple()
{
    boost::shared_ptr<int>sp(new int(10));
    assert(sp.unique());//shared_ptr 是指针唯一持有者
    boost::shared_ptr<int>sp2=sp;
    assert(sp==sp2);
    cout<<sp.use_count()<<endl;
    *sp2=100;
    cout<<*sp<<endl;
    sp.reset();
    cout<<sp.use_count()<<endl;


}
int main()
{
    boost::shared_ptr<int>p(new int(100));
    shared s1(p),s2(p);
    s1.print();
    s2.print();

    *p=20;
    print_funv(p);
    s1.print();

    simple();
}

//
count:3 v=100
count:3 v=100
count:4 v=20
count:3 v=20
2
100
0

在声明了shared_ptr和两个shared类实例后,指针被它们所共享,因此引用计数为3。print_func()函数内部拷贝了一个shared_ptr对象,因此引用计数再增加1,但当退出函数时铂贝自动析构,引用计数又恢复为3。

工厂函数

因为shared ptr的构造还需要new调用,这导致了代码中的某种不对称性。虽然’shared ptr很好地包装了new表达式,但过多的显式new操作符也是个问题,显式new调用应该使用工厂模式来解决。

template<typename T,typename... Args>
typename boost::detail::sp_if_not_array<T>::type
make_shared(Args&&.. args)

auto sp= make_shared<string>("make_shared");

应用

应用与标准容器

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

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

 using vs=vector<boost::shared_ptr<int>> ;
   vs v(10);
   int i=0;
   for(auto pos=v.begin();pos!=v.end();++pos)
    {
        (*pos)=boost::make_shared<int>(++i);
        cout<<*(*pos)<<", ";
    } 
    cout<<endl;
    boost::shared_ptr<int> p=v[9];
    *p=100;
    cout<<*v[9]<<endl;
//
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
100

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

应用于桥接模式

桥接模式(Bridge)是一种结构型设计模式,它把类的具体实现细节对用户隐藏起来,以达到类之间的最小耦合关系。在具体编程实践中桥接模式也被称为 pimpl或者 handle/body惯用法,它可以将头文件的依赖关系降到最小,减少编译时间,而且可以不使用虚函数实现多态。

#include<boost/smart_ptr.hpp>
#include<boost/make_shared.hpp>
#include<string>
#include<string>
#include<vector>
using namespace boost;

class sample
{
 private:
 class impl;
 shared_ptr<impl> p;
 public:
 sample();
 void print();
};

class sample::impl
{
 public:
 void print()
 {
    std::cout<<"impl print";
 }
};
 sample::sample():p(new impl)
{}
void sample::print()
{
    p->print();
}

int main()
{
    sample s;
    s.print();
}

定时删除器

shared ptr(Y * p,D d)的第一个参数是要被管理的指针,它的含义与其他构造函数的参数相同。而第二个删除器参数d则告诉shared_ptr在析构时不是使用delete来操作指针p,而要用d来操作,即把delete p换成d(p)。

网络编程

socket *s =open_socket();

shared_ptr<socket_t>p(s,close_socket());

 这样我们就使用shared ptr配合定制的删除器管理了socket资源。当离开作用域时,shared_ptr 会自动调用close_socket ()函数关闭socket,再也不会有资源遗失的担心。

weak_ptr

weak_ptr是为配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和->。它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。

类摘要:

template<typename class T>
class weak_ptr
{
    public:
    weak_ptr();
    template(class Y)weak_ptr(shared_ptr<Y>const &r);
    ~weak_ptr();
    weak_ptr& operator=(weak_ptr const &r);
    long use_count() const;//计数
    bool expired() const ;
    shared_ptr<T> lock const;
    ....
}

weak_ptr被设计为与shared_ptr协同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。同样,weak ptr析构时也不会导致引用计数减少,它只是一个静静的观察者。

使用weak_ptr 的成员函数 use_count ()可以观测资源的引用计数,另一个成员函数expired ()的功能等价于 use_count ( ) ==0,但更快,表示被观测的资源(也就是被shared_ptr管理的资源)已经不复存在。

实例:

#include<boost/smart_ptr.hpp>
#include<boost/make_shared.hpp>
#include<string>
#include<assert.h>
using namespace boost;
void simple()
{
    
   shared_ptr<int> sp(new int(10));
   assert(sp.use_count());

   weak_ptr<int>wp(sp);
   std::cout<<wp.use_count()<<std::endl;
   if(!wp.expired())
   {
    shared_ptr<int> sp2=wp.lock();
    *sp=10;
    std::cout<<sp.use_count()<<std::endl;
   }
   std::cout<<wp.use_count()<<std::endl;
   sp.reset();
   if(wp.expired())
   {
      std::cout<<"nullptr"<<std::endl;
   }

}

int main()
{
   simple();
}
//
1
2
1
nullptr

对象自我管理

weak_ptr的一个重要用途是获得this 指针的 shared_ptr,使对象自己能够生产shared_ptr管理自己:对象使用weak_ptr观测this 指针,这并不影响引用计数,在需要的时候就调用lock ()函数,返回一个符合要求的shared_ptr供外界使用。

#include<boost/smart_ptr.hpp>
#include<boost/make_shared.hpp>
#include<string>
#include<assert.h>
#include<boost/enable_shared_from_this.hpp>
using namespace boost;
class self_shared:public enable_shared_from_this<self_shared>
{
    public:
    self_shared(int n):x(n){}
    int x;
    void print()
    {
        std::cout<<"self_shared"<<std::endl;
        std::cout<<x<<std::endl;
    }

};

void object()
{
    auto sp=make_shared<self_shared>(313);
    sp->print();
    auto p=sp->shared_from_this();
    p->x=1000;
    p->print();
}
int main()
{
   object();
}
//
self_shared
313
self_shared
1000

需要注意的是千万不能对一个普通对象(非 shared_ptr管理的对象)使用shared_from this(获取shared ptr,例如:

self_share ss;

auto p=ss.shared_from_this();

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/129677.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!