智能指针
RAII
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内
存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在
对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做
法有两大好处:
- 不需要显式地释放资源。
- 采用这种方式,对象所需的资源在其生命期内始终保持有效
我们先来看一下下面的代码:
template<class T>
class SmartPtr
{
public:
SmartPtr(T *ptr)
:_ptr(ptr)
{}
~SmartPtr()
{
delete[] _ptr;
cout << "delete:" << _ptr << endl;
}
private:
T* _ptr;
};
int main()
{
SmartPtr<int> sp1(new int[10]);
return 0;
}
运行结果:
这样存在了在结束后就会自动释放内存;
但是上述SmartPtr并不能称为智能指针,因为它还不具有指针的行为。指针可以解引用,也可
以通过->去访问所指空间中的内容,因此:AutoPtr模板类中还得需要将* 、->重载下,才可让其
像指针一样去使用;
但是智能指针最大的问题就是拷贝问题:
int* p1 = new int[10];
int* p2 = p1;
指针本身就是浅拷贝,p1和p2共同管理同一块资源;
但是这样就会造成析构两次;
那拷贝问题如何解决?
auto_ptr
特点:管理权转移,被拷贝对象被悬空,有风险,不建议使用
最早期提出的是auto_ptr:
auto_ptr<int> sp1(new int[10]);
//c++98 转移管理权 --->sp1对象悬空
auto_ptr<int> sp2(sp1);
unique_ptr
特点:不支持拷贝,没有风险,建议使用
在c++98到c++11之间有一个过度:boost库
在unique_ptr的原理特别的简单粗暴,就是防拷贝:
unique_ptr (const unique_ptr&) = delete;
class A
{
public:
~A()
{
cout << "~A()" << endl;
}
private:
int _a1 = 1;
int _a2 = 1;
};
int main()
{
unique_ptr<A> sp1(new A);
//不支持拷贝
//unique_ptr<A> sp2(sp1);
A* p = sp1.get();//获得到原身指针;
cout << p << endl;
return 0;
}
shared_ptr
但是在一些情况下必须使用拷贝,所以又延伸出了shared_ptr;
shared_ptr就支持拷贝,不会报错:
原理:
下面我们来监视一下引用计数:
make_shared
和shared_ptr主要区别:把资源和计数绑到一起
手撕shared_ptr⭐⭐⭐⭐⭐
手撕shared_ptr主要就是引用计数的处理:
最复杂的是赋值:
release()函数就是析构函数里面的,只不过这里给他重新封装了;
尤其要注意自己给自己赋值的情况⭐⭐⭐⭐
weak_ptr
不支持RAII,不单独管理资源;辅助解决,shared_ptr循环引用,本质赋值或拷贝时,只指向资源,但是不增加引用计数
struct Node {
std::weak_ptr<Node> _next;//改成weak_ptr,weak_ptr不增加节点的引用计数
std::weak_ptr<Node> _prev;
int _val;
~Node()
{
cout << "~Node()" << endl;
}
};
void test01()
{
std::shared_ptr<Node> p1(new Node);
std::shared_ptr<Node> p2(new Node);
p1->_next = p2;
p2->_prev = p1;
}