• C++ 智能指针如何实现智能?


     本文来自公众号“AI大道理”。

    这里既有AI,又有生活大道理,无数渺小的思考填满了一生。

    指针是一个变量,其值为另一个变量的地址,即内存位置的直接地址。

    指针就像是一个目录,其值就像页码,页码指向某一个的内容。

    1、普通指针

    问题1:内存泄露问题

    问题描述:

    C++没有自动回收内存的机制,每次new出来的动态内存必须手动delete。

    如果忘记delete,资源未被释放,将导致内存泄露。

    问题2:迷途指针

    问题描述:

    多个指针指向同一个对象时,有一个指针释放了,但是其他指针并不知道这个情况,若继续使用那个被释放的指针将出错。

    问题3:野指针

    没有经过初始化就直接拿来用的指针,将出错。

    2、智能指针

    智能指针是一个模板类,用来存储指针(指向动态分配对象的指针)。

    智能指针是通过基本类型(模板类)指针构造类的对象,指针本身就是一个自定义的对象。

    当此对象被销毁时,即调用此对象的析构函数,释放此指针。

    智能指针其实就是对普通指针的封装,封装成一个类。通过重载*和->两个运算符使得智能指针表现得就像普通指针一样。

    智能指针遵从RAII思想。

    RAII思想:

    能够像指针一样(运算符重载,解引用,指向对象成员)。

    对资源进行封装和管理。

    RAII思想(资源分配及初始化)(Resource Acquisition Is Initialization)

    • 定义一个类来封装资源的分配与释放。

    • 构造函数中完成资源的分配及初始化。

    • 析构函数中完成资源的清理,可以保证资源的正确初始化和释放

    • 如果对象是用声明的方式在栈上创建局部对象,那么RAII机制就会正常工作,当离开作用域对象会自动销毁而调用析构函数释放资源。

    智能指针主要有:

    auto_ptr:弃用的指针

    unique_ptr:独占智能指针

    shared_ptr: 共享智能指针

    weak_ptr: 弱智能指针

    3、auto_ptr

    实现原理:

    auto_ptr事实上是一个类,在构造对象时获取对象的管理权,无需考虑释放动态内存开辟的空间,在析构函数中直接释放,不会出现内存泄漏的问题。

    缺陷:
    1)一个指针变量指向的空间不能由两个auto_ptr管理,不然会析构两次,使程序崩溃。

    2)auto_ptr的拷贝构造,将源指针的管理权交给目标指针,会使得源指针悬空,解引用是会出现很多问题。

    3)auto_ptr不能用来管理数组,析构函数中用的是delete。

    4、unique_ptr

    unique_ptr解决了auto_ptr编译不报错的问题。

    unique_ptr是auto_ptr的继承者,对于同一块内存只能有一个持有者。

    unique_ptr和auto_ptr唯一区别就是unique_ptr不允许赋值操作,也就是不能放在等号的右边,这一定程度避免了一些误操作导致指针所有权转移。

    unique_ptr的核心特点就如它的名字一样,它拥有对持有对象的唯一所有权,即两个unique_ptr不能同时指向同一个对象。

    nique_ptr所持有的对象只能通过转移语义将所有权转移到另外一个unique_ptr。

    unique_ptr本身拥有的方法主要包括:

    • get()获取其保存的原生指针,尽量不要使用

    • bool()判断是否拥有指针

    • release()释放所管理指针的所有权,返回原生指针。但并不销毁原生指针。

    • reset()释放并销毁原生指针。如果参数为一个新指针,将管理这个新指针

    5、shared_ptr

    unique_ptr不能多个指针指向同一个资源,而shared_ptr可以。

    实现原理:

    每次复制或者多一个共享内存资源的shared_ptr时,计数+1;

    每次释放shared_ptr时,计数-1;

    当shared_ptr计数为0时,证明所有指向同一资源的shared_ptr都全部释放了。

    shared_ptr 需要维护的信息有两部分:

    • 指向共享资源的指针。

    • 引用计数等共享资源的控制信息——实现上是维护一个指向控制信息的指针。

    常规的创建一个 shared_ptr:

    为了构建一个std::shared_ptr对象,却进行了两次内存分配,而且第二次内存分配分配的内存还比较小,这一方面会影响程序性能,另一方面还会大大增加内存碎片产生的可能性。

    复制一个 shared_ptr :

    std::make_shared创建一个 shared_ptr:

    std::make_shared的精妙之处就在于,它将std::shared_ptr构造中的两次内存分配降低到了一次。这会对提供程序性能和降低内存碎片都有帮助。

    shared_ptr本身拥有的方法主要包括:

    • get() 获取其保存的原生指针,尽量不要使用

    • bool() 判断是否拥有指针

    • reset() 释放并销毁原生指针。如果参数为一个新指针,将管理这个新指针

    • unique() 如果引用计数为 1,则返回 true,否则返回 false

    • use_count() 返回引用计数的大小

    循环引用问题:

    但是,shared_ptr也有一个致命的缺点,就是会出现循环引用
    Shared_ptr 会出现循环引用的情况:

    调用析构后,还有互相引用的指针计数没有减掉。

    要释放sp2,就需要先释放sp1->_next。

    要释放sp1->_next, 就需要先释放sp1。

    要释放sp1,就需要先释放sp2->_prev。

    要释放sp2->_prev,就需要先释放sp2。

    这样一来,就陷入了一个无限的循环当中,谁都释放不掉。

    如何解决?

    6、weak_ptr

    std::weak_ptr 要与 std::shared_ptr 一起使用。

    弱引用指针就是没有“所有权”的指针。

    有时候我们只是想找个指向这块内存的指针,但我们不想把这块内存的生命周期与这个指针关联,这种情况下,弱引用指针就代表“我指向这东西,但这东西什么时候释放不关我事儿。

    weak_ptr是辅助shared_ptr的存在,它只提供对管理对象的访问手段,同时可以实时动态知道所指向的对象是否存活,起到观察者的作用。

    weak_ptr不具有普通指针的行为,没有重载operator *和->,只能想像旁观者一样观测资源的使用情况。

    实现原理:

    计数区域引进新的计数变量weak_count,来作为弱引用指针。

    weak_ptr的构造和析构不会引起shared_ptr的计数的增加和减少,只会引起weak_count的增加和减少。

    双计数:

    资源的释放只取决shared的计数,当计数为0时,释放资源,weak_ptr不控制资源的生命周期。

    计数区域的释放取决于shared计数和weak计数,当两者都为0时,才释放计数区域。

    weak_ptr本身拥有的方法主要包括:

    • expired() 判断所指向的原生指针是否被释放,如果被释放了返回 true,否则返回 false

    • use_count() 返回原生指针的引用计数

    • lock() 返回 shared_ptr,如果原生指针没有被释放,则返回一个非空的 shared_ptr,否则返回一个空的 shared_ptr

    • reset() 将本身置空

    循环引用问题的解决:

    调用析构后,sp1和sp2成功释放。

    要释放sp1->_next, 就需要先释放sp1,已经释放,所以sp1->_next释放。

    要释放sp2->_prev,就需要先释放sp2,已经释放,所以sp2->_prev释放。

    这样一来,就都释放掉了。

    7、总结

    unique_ptr:内存的所有者或者说管理者必须是唯一的。如果进入不同的模块或者调用者,那么执行所有权转移。

    shared_ptr: 内存由多个指针变量共同使用,共同拥有内存的所有权。但是必须杜绝循环拷贝!

    weak_ptr: 对内存的使用仅仅是访问而已,不涉及其生命周期的管理。


     

     ——————

    浅谈则止,细致入微AI大道理

    扫描下方“AI大道理”,选择“关注”公众号

    —————————————————————

    —————————————————————

    投稿吧   | 留言吧


  • 相关阅读:
    PyQt5 -1 最基本的小窗口
    浅谈线段树
    最小生成树问题
    最短路问题
    多重背包问题
    02背包(嘻嘻,完全背包)
    01背包例题
    背包问题(好奇怪)
    关于深搜及广搜
    搜索回溯(第二)
  • 原文地址:https://www.cnblogs.com/AIBigTruth/p/15841091.html
Copyright © 2020-2023  润新知