计算机系统中资源有很多种,内存是我们最常用到的,此外还有文件描述符、socket、操作系统handle、数据库连接等,程序里申请这些资源后必须及时归还系统,否则就会产生难以预料的后果。
为了管理内存等资源,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是一个很类似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
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
- #include
- using namespace boost;
-
- int main()
- {
- scoped_ptr
sp(new std::string("text")); - assert(sp);
- assert(sp!=nullptr);
- std::cout<<*sp<
- std::cout<
size()< - }
- //
- text
- 4
错误实例
- scoped_ptr
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
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
- #include
- 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:"<
use_count()<< - "v="<<*p<
- }
-
-
- };
- void print_funv(boost::shared_ptr<int>p)
- {
- cout<<"count:"<
use_count()<< - "v="<<*p<
- }
-
- 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<
use_count()< - *sp2=100;
- cout<<*sp<
- sp.reset();
- cout<
use_count()< -
-
- }
- 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
::type - make_shared(Args&&.. args)
-
- auto sp= make_shared
("make_shared");
应用
应用与标准容器
一种用法是将容器作为shared ptr管理的对象,如shared ptr>,使容器可以被安全地共享,用法与普通shared _ptr没有区别,我们不再讨论。
另一种用法是将 shared_ptr作为容器的元素,如vector>,因为shared_ptr支持拷贝语义和比较操作,符合标准容器对元素的要求,所以可以在容器中安全地容纳元素的指针而不是拷贝:
- using vs=vector
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<
- boost::shared_ptr<int> p=v[9];
- *p=100;
- cout<<*v[9]<
- //
- 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
- #include
- #include
- #include
- #include
- using namespace boost;
-
- class sample
- {
- private:
- class impl;
- shared_ptr
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_ptrp(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
const &r); - ~weak_ptr();
- weak_ptr& operator=(weak_ptr const &r);
- long use_count() const;//计数
- bool expired() const ;
- shared_ptr
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
- #include
- #include
- #include
- using namespace boost;
- void simple()
- {
-
- shared_ptr<int> sp(new int(10));
- assert(sp.use_count());
-
- weak_ptr<int>wp(sp);
- std::cout<
use_count()< - if(!wp.expired())
- {
- shared_ptr<int> sp2=wp.lock();
- *sp=10;
- std::cout<
use_count()< - }
- std::cout<
use_count()< - sp.reset();
- if(wp.expired())
- {
- std::cout<<"nullptr"<
- }
-
- }
-
- int main()
- {
- simple();
- }
- //
- 1
- 2
- 1
- nullptr
对象自我管理
weak_ptr的一个重要用途是获得this 指针的 shared_ptr,使对象自己能够生产shared_ptr管理自己:对象使用weak_ptr观测this 指针,这并不影响引用计数,在需要的时候就调用lock ()函数,返回一个符合要求的shared_ptr供外界使用。
- #include
- #include
- #include
- #include
- #include
- using namespace boost;
- class self_shared:public enable_shared_from_this
- {
- public:
- self_shared(int n):x(n){}
- int x;
- void print()
- {
- std::cout<<"self_shared"<
- std::cout<
- }
-
- };
-
- void object()
- {
- auto sp=make_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();
-
相关阅读:
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
-
最新文章
-
沪漂五周年了:我越来越迷茫了
Agentic Skill Routing 实战:别再把所有 Skill 塞进 AI Agent 上下文
MySQL-Seconds_behind_master的精度误差
[MAF预定义ChatClient中间件-03]CachingChatClient——利用缓存省钱省时间
AI的至暗历史:从万众期待到被政府撤资,AI的两次死亡徘徊
Agent OS :五种驯服不确定性的范式
PortSwigger SQL注入LAB11
数据库即时编译JIT
[Begin]AI Learn Data Day 0
深度学习进阶(二十七)现代 LLM 的核心架构设计其二:SwiGLU