• boost之内存管理


    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

    using namespacce boost;

    scoped_ptr

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

    类摘要:

    1. template<typename T>
    2. class scoped_ptr{
    3. private:
    4. T* p;
    5. scoped_ptr(scoped_ptr const & );//拷贝私有化
    6. scoped_ptr&=(scoped_ptr const &);//赋值私有化
    7. void operator==(scoped_ptr const&)const;
    8. void operator!=(scoped_ptr const&)const;
    9. public:
    10. void reset(T*p=0);
    11. T& operator*()const ;
    12. T& operator->()const;
    13. T* get()const; //获取原始指针
    14. explicit operator bool() const;
    15. void swap(scoped_ptr &b);
    16. };
    17. template<typename T>inline
    18. bool operator==(scoped_ptrconst &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 意图,不做深入探究

    实例:

    1. #include
    2. #include
    3. using namespace boost;
    4. int main()
    5. {
    6. scoped_ptrsp(new std::string("text"));
    7. assert(sp);
    8. assert(sp!=nullptr);
    9. std::cout<<*sp<
    10. std::cout<size()<
    11. }
    12. //
    13. text
    14. 4

    错误实例

    1. scoped_ptr sp2=sp;
    2. //scoped_ptr 不能拷贝构造
    3. class ptr_owned final
    4. {
    5. scoped_ptr<int>m_ptr;
    6. };
    7. ptr_owned p;
    8. ptr_owned p2(p);
    9. scoped_ptr 不能拷贝构造

    share_prt(简单介绍)

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

    类摘要:

    1. template<typename T>
    2. class shared_ptr
    3. {
    4. public:
    5. shared_ptr();
    6. template<typename Y>explicit shared_ptr(Y* p);
    7. template<typename Y,typename D>shared_ptr(Y*p,D d);
    8. ~shared_ptr();
    9. shared_ptr(shared_ptr const & r);
    10. shared_ptr &operator==(shared_ptr const& r);
    11. template<class Y>shared_ptr&operator=(shared_ptrconst &r);
    12. .....
    13. };

    操作函数

    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的同时改为管理另一个指针。

    实例:

    1. #include
    2. #include
    3. using namespace boost;
    4. using namespace std;
    5. class shared
    6. {
    7. private:
    8. boost::shared_ptr<int>p;
    9. public:
    10. shared(boost::shared_ptr<int>p_):p(p_){}
    11. void print()
    12. {
    13. cout<<"count:"<use_count()<<
    14. "v="<<*p<
    15. }
    16. };
    17. void print_funv(boost::shared_ptr<int>p)
    18. {
    19. cout<<"count:"<use_count()<<
    20. "v="<<*p<
    21. }
    22. void simple()
    23. {
    24. boost::shared_ptr<int>sp(new int(10));
    25. assert(sp.unique());//shared_ptr 是指针唯一持有者
    26. boost::shared_ptr<int>sp2=sp;
    27. assert(sp==sp2);
    28. cout<use_count()<
    29. *sp2=100;
    30. cout<<*sp<
    31. sp.reset();
    32. cout<use_count()<
    33. }
    34. int main()
    35. {
    36. boost::shared_ptr<int>p(new int(100));
    37. shared s1(p),s2(p);
    38. s1.print();
    39. s2.print();
    40. *p=20;
    41. print_funv(p);
    42. s1.print();
    43. simple();
    44. }
    45. //
    46. count:3 v=100
    47. count:3 v=100
    48. count:4 v=20
    49. count:3 v=20
    50. 2
    51. 100
    52. 0

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

    工厂函数

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

    1. template<typename T,typename... Args>
    2. typename boost::detail::sp_if_not_array::type
    3. make_shared(Args&&.. args)
    4. auto sp= make_shared("make_shared");

    应用

    应用与标准容器

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

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

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

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

    应用于桥接模式

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

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. using namespace boost;
    7. class sample
    8. {
    9. private:
    10. class impl;
    11. shared_ptr p;
    12. public:
    13. sample();
    14. void print();
    15. };
    16. class sample::impl
    17. {
    18. public:
    19. void print()
    20. {
    21. std::cout<<"impl print";
    22. }
    23. };
    24. sample::sample():p(new impl)
    25. {}
    26. void sample::print()
    27. {
    28. p->print();
    29. }
    30. int main()
    31. {
    32. sample s;
    33. s.print();
    34. }

    定时删除器

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

    网络编程

    socket *s =open_socket();

    shared_ptrp(s,close_socket());

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

    weak_ptr

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

    类摘要:

    1. template<typename class T>
    2. class weak_ptr
    3. {
    4. public:
    5. weak_ptr();
    6. template(class Y)weak_ptr(shared_ptrconst &r);
    7. ~weak_ptr();
    8. weak_ptr& operator=(weak_ptr const &r);
    9. long use_count() const;//计数
    10. bool expired() const ;
    11. shared_ptr lock const;
    12. ....
    13. }

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

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

    实例:

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace boost;
    6. void simple()
    7. {
    8. shared_ptr<int> sp(new int(10));
    9. assert(sp.use_count());
    10. weak_ptr<int>wp(sp);
    11. std::cout<use_count()<
    12. if(!wp.expired())
    13. {
    14. shared_ptr<int> sp2=wp.lock();
    15. *sp=10;
    16. std::cout<use_count()<
    17. }
    18. std::cout<use_count()<
    19. sp.reset();
    20. if(wp.expired())
    21. {
    22. std::cout<<"nullptr"<
    23. }
    24. }
    25. int main()
    26. {
    27. simple();
    28. }
    29. //
    30. 1
    31. 2
    32. 1
    33. nullptr

    对象自我管理

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

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. using namespace boost;
    7. class self_shared:public enable_shared_from_this
    8. {
    9. public:
    10. self_shared(int n):x(n){}
    11. int x;
    12. void print()
    13. {
    14. std::cout<<"self_shared"<
    15. std::cout<
    16. }
    17. };
    18. void object()
    19. {
    20. auto sp=make_shared(313);
    21. sp->print();
    22. auto p=sp->shared_from_this();
    23. p->x=1000;
    24. p->print();
    25. }
    26. int main()
    27. {
    28. object();
    29. }
    30. //
    31. self_shared
    32. 313
    33. self_shared
    34. 1000

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

    self_share ss;

    auto p=ss.shared_from_this();

  • 相关阅读:
    nodejs+vue+elementui网上书城 图书销售商城网站express
    39. Mybatis面试18问,你想知道的都在这里了
    93.(cesium之家)cesium动态单体化-倾斜摄影(楼栋)
    六、单例模式
    【Java线程池】 java.util.concurrent.ThreadPoolExecutor 源码分析
    C. Even Picture(构造)
    如何创建springboot项目
    SpringMVC-SSM整合/分页插件PageHelper案例
    IAST技术进阶系列(五):共生进化,自适应云原生
    java-php-python-智能拼车系统计算机毕业设计
  • 原文地址:https://blog.csdn.net/qq_62309585/article/details/126812765