智能指针学习

Posted on 2023-05-20 22:15  玄灵镜  阅读(14)  评论(0编辑  收藏  举报

c++的抛异常解决了返回错误码与函数层层返回的问题,但是异常往往可能有很多种,也有可能前面开辟了很多个空间,之后随着抛异常,这些空间都等着释放,这就太麻烦了,有可能会在写代码的时候疏忽忘记了释放这块空间造成内存泄漏,

所以智能指针可以很好的处理这种情况,我们知道c++中的类可以在其生命周期结束时自动调用他的析构函数,不如用一个自定义类去封装这个指针让其在出了作用域过后自己析构自己,然后这个类来充当指针的作用,重载解引用运算符和指针运算符,这样就解决了人力无法顾及到一些指针释放的问题了.

需要注意的是智能指针不可以去拷贝构造,和使用深拷贝运算符,因为这个类是代理管理这个指针,如果拷贝出来一个另外的指针,这算什么?,算弄出来一个没用的空间?也不可以浅拷贝,这关乎到哪一个指针有析构的权利这个问题,开辟空间的地址只有一份,若是有很多个地方都在用这个智能指针,一个析构了就全崩了.

所以为了解决这个问题有了

auto_ptr:他是通过转移拷贝来解决这个问题的,当拷贝构造或者赋值运算符时,将自身的空间交给另一个,自己空间置为空,就防止了多个智能指针指向同一个空间的问题了.但是这样原来的指针为空万一访问了就坏了,所以又有了unique_ptr.

unique_ptr:这个智能指针会更加直接,他直接将拷贝构造私有化并用delete修饰,这样就不会有人调用拷贝构造的风险了.但是如果非要拷贝怎么办呢.又有了shared_ptr.

shared_ptr:以上废了好大功夫禁止使用拷贝构造归根结底就是防止指针在不该析构的时候被析构,于是shared_ptr用了一个指针变量作为成员便利那个来记录这个指针有多少个对象在使用,他只在有参或无参构造时开辟空间,拷贝构造时就给这个计数器加一,析构时判断这个计数器,是否为零,如果为零就释放空间,如果不为零就计数器减一,这样就可以同时存在多个指向一个指针的智能指针了。

在编程中往往可能会遇到多线程编程的问题:这个shared_ptr在一个线程中运行时没问题,若是在多线程中使用,就会出现线程安全问题,因为这个计数器的加一并不是原子指令,他有可能被多个线程同时访问,导致计算不准确,这时就需要上锁了:用锁将计数器的计算过程锁起来后,shared_ptr就线程安全了。

 这里将这个锁放在成员变量里,用的时候更加方便。

上面的share_ptr看着已经完善了,但是在使用的过程中发现还是有缺陷,比如循环引用问题》

此时我需要自定义一个双向链表,因为种种原因链表的指针需要是智能指针,因此结点中的两个指针域也应该为智能指针,为了同种类型相互兼容,当初始化两个节点后此时两个个智能指针的计数器都为一,但是当两个结点相互连接后,因为赋值运算符的浅拷贝,是各自的计数器都变成了2,这是如果析构第一个结点,和第二个节点他们析构完了结点各自减少了一次,此时析构完成但是计数器并没有归零,所以空间没有释放,又出现问题了。

这时有有了weak_ptr,他的功能就是让两个shared_ptr指针进行浅拷贝但是不增加计数器,他的构造函数参数可以是一个share_ptr,通过传入一个shared_ptr,让他指向这个shared_ptr代理的指针,完成拷贝,这其中需要解决访问权限问题,因为weak无法访问shared的私有成员变量。

所以可以将双向链表的指针域换成weak_ptr类型就可以连接上同时不增加shared的计数器。这样就解决了循环引用的问题。

但是平时再开辟空间时并不只是开辟一个空间有时会开辟一段连续的空间,这又怎么办呢?用delete释放delete【】会出问题的

可以用把智能指针的析构函数作为模板参数,在外面实现,因为释放数组与释放一个空间不同的地方在于析构函数的不同。这是不仅可以处理delete也可以处理delete[],mallic和文件指针。

 

Copyright © 2024 玄灵镜
Powered by .NET 8.0 on Kubernetes