1.智能指针的实现原理:
定义一个对象的时候会在构造函数里将我传入的指针的值为智能指针对象底层管理的指针初始化,在出了大括号后调用析构函数,释放指针所管理的资源并将指针置为空。
2.智能指针的分类与实现原理:
(1)不带引用计数的智能指针:auto_ptr,scoped_ptr、unique_ptr
auto_ptr:将传入的对象的底层指针给我本对象的底层指针、并将被拷贝(赋值)对象的底层指针置空。这样对于用户是不友好的,因为用户传入一个对象根本就不知道传入的对象将会失效。
scoped_ptr:将左值拷贝构造与左值拷贝赋值给delete,从根本上杜绝了两根指针指向同一份资源带来资源泄漏问题。(scoped_ptr好像visual studio2023不支持,所以这边没贴图片了)
unique_ptr(最常用):将左值拷贝构造与左值拷贝赋值给delete,添加右值拷贝构造与右值拷贝赋值。这样对于用户是友好的,因为用户想传参,比如得move一下,此时用户是知道我传入的对象即将失效。
(2)带引用计数的智能指针:shared_ptr、weak_ptr
shared_ptr:增加一个管理引用计数的对象作为成员指针,这个引用计数的对象所对应的类底层是对引用计数的封装
下面我们通过代码演示它的实现原理:
usingnamespacestd; //对资源进行引用计数的类template<typenameT>classRefCnt{ public: RefCnt(T*ptr=nullptr) :mptr(ptr) { if (mptr!=nullptr) mcount=1; } voidaddRef() { mcount++;//增加资源的引用计数 } intdelRef() { return--mcount; } private: T*mptr; intmcount; }; template<typenameT>classCSmartPtr{ public: CSmartPtr(T*ptr=nullptr) :mptr(ptr) { mpRefCnt=newRefCnt<T>(mptr); } ~CSmartPtr() { if (0==mpRefCnt->delRef()) { deletemptr; mptr=nullptr; } } CSmartPtr(constCSmartPtr<T>&src) :mptr(src.mptr), mpRefCnt(src.mpRefCnt) { if (mptr!=nullptr) { mpRefCnt->addRef(); } } CSmartPtr<T>&operator=(constCSmartPtr<T>&src) { if (this==&src) { return*this; } if (0==mpRefCnt->delRef()) { deletemptr; } mptr=src.mptr; mpRefCnt=src.mpRefCnt; mpRefCnt->addRef(); return*this; } T&operator*() { return*mptr; } T*operator->() { returnmptr; } private: T*mptr;//指向资源的指针RefCnt<T>*mpRefCnt;//指向该资源引用计数对象的指针}; intmain(void) { CSmartPtr<int>ptr1(newint); CSmartPtr<int>ptr2=ptr1; return0; }
在这段代码中,给每一个对象资源,就会匹配一个引用计数对象指针,并将底层的引用计数置为1。当智能指针使用资源的时候,引用计数+1;当智能指针不使用资源的时候,引用计数-1;引用计数等于0的时候开始释放资源。
weak_ptr:只能观察资源,不能改变资源的引用计数,不能访问资源。
同样的我们通过一段代码来演示weak_ptr的用法。
usingnamespacestd; classB; classA{ public: A() { cout<<"A()"<<endl; } ~A() { cout<<"~A()"<<endl; } voidtestA() { cout<<"非常好用的方法"<<endl; } weak_ptr<B>_ptrb; }; classB{ public: B() { cout<<"B()"<<endl; } ~B() { cout<<"~B()"<<endl; } voidfunc() { shared_ptr<A>ps=_ptra.lock(); if (ps!=nullptr) { ps->testA(); } } weak_ptr<A>_ptra; }; intmain(void) { shared_ptr<A>pa(newA()); shared_ptr<B>pb(newB()); pa->_ptrb=pb; pb->_ptra=pa; cout<<pa.use_count() <<endl; cout<<pb.use_count() <<endl; pb->func(); return0; }
在这一段代码中,weak_ptr的引用并不会改变对象资源的引用计数,如果想让它能够访问资源需要调用.lock()方法把它提升成强智能指针。
这种手法也被用来解决shared_ptr的交叉引用问题以及多线程访问共享对象的线程安全问题。在后面我们会有两期分别对这两个问题进行讲解。