内存管理技术分享文稿记录v01

内存管理专题-智能指针

智能指针概念和分类

  • 智能指针作为内存管理的一种常用方式,它持有的指针对象

  • 2011 分析比较 https://blog.csdn.net/dbzhang800/article/details/6403285

    • Qt家族的智能指针介绍
    • 引入的历史和版本
    • 从一个案例讲解使用
    • 讲解qt版本和std版本的智能指针之间的区别联系
  • 使用注意的3个考虑点

    • 来判断这个 std::auto_ptr时候
    • 不能让多个auto_ptr 指向同一个对象!(auto_ptr被销毁时会自动删除它指向的对象,这样对象会被删除多次):考虑多个智能指针释放能指向同一个对象
    • 通过拷贝构造或赋值进行操作时,被拷贝的会自动变成NULL,复制所得的指针将获得资源的唯一所有权:考虑拷贝和复制函数是否是开放的
    • 智能指针不能指向数组(因为其实现中调用的是delete而非delete[]) : 这个应该是共性限制,有专门针对数组的智能指针
    • 智能指针不能作为容器类的元素。: 这个判断应该是基于,该智能指针是否有公有的拷贝和赋值函数。
    • 在C++0x中,auto_ptr已经不建议使用,以后应该会被其他3个智能指针所取代 ?所有权问题
  • C++ 智能指针最佳实践&源码分析 场景和注意事项说的很好

    • https://zhuanlan.zhihu.com/p/479310894
  • 分类

    • 引用计数型智能指针
  • QPointer

    • 需要注意的是:QPointer所指向的对象必须是QObject或其派生类对象。 因为其对象析构时会执行QObject的析构函数,进而执行QObjectPrivate::clearGuards(this); 本质
    • 通常情况下,我们在手动delete一个指针的时候,需要再将其置空,要不然会变成一个悬挂的野指针,那么QPointer就是帮忙干这事的,会在对象被销毁时,自动设置为NULL。原理是什么,除了上面说的调用clearGurards(this)。
  • 其他讲解:

    • Qt的智能指针主要有QSharedPointer和QScopedPointer,当然还有其它的类,但是用得较少。QSharedPointer是在Qt4.5的时候引进的,而QScopedPointer是在Qt4.6的时候引进的,我想这是因为Qt开发者看到了Boost或是Loki在智能指针这方面的卓越研究,而决定自己制作一套强大的智能指针吧。Qt的智能指针风格类似Boost,为每种指针提供了一个从字面上很好理解的类来进行封装。

具体主要的几个智能指针基本原理和用法,使用场景

使用场景
  • 正常使用举例A

  • 一直没有合适的项目场景来使用智能指针,现在出现了这么一个 场景

    • A类和B类,B类有引用A类中的堆对象成员,当A类中的对象被释放的时候,B类对象保存的相关引用信息,自动更新为已经释放状态,避免使用野指针。 - https://blog.csdn.net/zhurui3747/article/details/79609157
    • 使用强智能指针和弱智能指针能够方便的管理内存,如果将强智能指针清空(clear()),则其弱引用可以自动判断出其内存已被删除,弱引用自动变为NULL,不在需要程序员进行手动删除指针。
    • 此时对应的引用计数如下图,即强引用1次,弱引用2次(强智能指针占1次,弱智能占1次):
    • 在这里插入图片描述
  • 错误使用举例B

    • 使用场景注意,待自己编程测试 https://blog.csdn.net/wubw0585/article/details/95059030
    • 错误使用智能指针
    • 多个智能指针指向同一个内存地址,却每个都独立存在
    • `void MainWindow::doSemething4()
      {
      Person* p = new Person(“test2”);
      QSharedPointer p1(p );
      QSharedPointer p2(p );//不能都使用p进行赋值,智能指针清除时会将资源删除,导致多次析构报错。假如p1删除后,p2的资源也删除了
      QSharedPointer p3(p );
      p1.clear();
      p2.clear();//从这里开始,就会报错
      p3.clear();
      qDebug()<<“clear all : delete p”;
      if(p1.isNull())
      qDebug()<<“p1 is null”;
      else
      qDebug()<<“p1 is not null”;
      }
      本地测试结果:
      在这里插入图片描述
  • 错误使用举例C

    • https://www.cnblogs.com/sky20080101/articles/8934484.html
    • 对于开始的引入介绍和自己使用出现的问题的讨论和评论
    • 楼上说的判断0xfeeefeee肯定是不行的,因为delete后清成feee这是VC的运行库在debug版本下的行为,在release版本中不会这么做,另外,使用QT的程序都是跨平台的,到了其他平台肯定也不是这样的。
      只有避免两次释放才是解决问题的正途。这个问题的原因是在QT的对象树系统中,删除父节点也会删除子节点,但是因为它的对象树中的对象并不是使用QSharedPointer管理的,而是使用的裸指针,这样你的QSharedPointer并不会得到通知而变成悬空指针。
      建议你对于放在对象树中的QObject对象,使用QPointer指针来保管,这样,既不会影响对象的正常释放,也可以在QObject释放时得到通知自动变成NULL,以后就可以判断是不是NULL来避免错误操作。 这种需要自己总结一下使用场景,然后采用什么指针方式(PS 本地需要编程来验证楼主的使用场景,以及新的方案可行性)
    • 待验证问题:
    • 使用QScopePointer,创建一个父对象为QObject类的对象,然后释放该子对象后,再释放父对象,是否会报错,qt父子对象系统,释放内存时候,是否会判断子对象释放存在,子对象的指针是否为null,对象系统中树状结构,里面保存的都是普通指针吧?
    • 使用QSharePointer,在上面的场景下,单独释放子对象又会怎么样,先释放父对象,再来释放子对象会有什么问题
    • 使用QPonter智能指针,在这三种场景下,释放完美解决问题。主要是在使用其他类创建的指针对象时候,使用类需要使用观察者型智能指针QPointer来判断,引用的对象释放已经释放。
  • C++智能指针的正确使用方式 https://www.cnblogs.com/codingmengmeng/p/13821603.html

    • 当你理不清引用关系的时候, 通常做法是 parent 类持有 child 的 shared_ptr, child 持有指向 parent 的 weak_ptr。这样也更符合语义
    • 讲解清晰,讲了unique_ptr, share_ptr的特点和使用场景,主要性能点(内存大小),原子操作性能低,使用移动优化性能(这样那还不是需要更精细的管理)
    • 对于智能指针的使用,实际上是对所有权和生命周期的思考,一旦想明白了这两点,那对智能指针的使用也就得心应手了。
        同时理解了每种智能指针背后的性能消耗,使用场景,那智能指针也不再是黑盒子和洪水猛兽
  • Qt浅谈之一:内存泄露(总结)

    • https://www.cnblogs.com/ghbjimmy/articles/5681980.html delete栈上的地址会出错
    • Qt中,最基础和核心的类是:QObject,QObject内部有一个list,会保存children,还有一个指针保存parent,当自己析构时,会自己从parent列表中删除并且析构所有的children。
    • parent被delete时,这个parent的相关所有child都会自动delete,不用用户手动处理。但parent是不区分它的child是new出来的还是在栈上分配的。这体现delete的强大,可以释放掉任何的对象,而delete栈上对象就会导致内存出错,这需要了解Qt的半自动的内存管理。 使用QLabel的小例子举例,讲解内存分配使用的问题,解决的方法。
    • 特别总结上面的解决方法为:Qt的半自动化的内存管理,qt自动垃圾回收机制
    • QObjectCleanupHandler, Qt 对象清理器是实现自动垃圾回收的很重要的一部分
    • QPointer的现实原理:在QPointer保存了一个QObject的指针,并把这个指针的指针(双指针)交给全局变量管理,而QObject 在销毁时(析构函数,QWidget是通过自己的析构函数的,而不是依赖QObject的)会调用QObjectPrivate::clearGuards 函数来把全局 GuardHash 的那个双指针置为*零,因为是双指针的问题,所以QPointer中指针当然也为零了。用isNull 判断就为空了。
  • QPointer里面有一个使用场景使用说明,告诉引用的变化情况

    • https://www.codeleading.com/article/82753736865/
  • 智能指针-使用、避坑和实现 画了思维导图总结特点

    • https://www.modb.pro/db/241321

智能指针性能测试分析

  • boost智能指针的性能测试
    • https://blog.csdn.net/runyon1982/article/details/49018673
    • 这个测试要小心编译器的优化,不然测试的结果会比较奇怪。
    • 测试结论:智能指针的构造代价比较高(拷贝构造)(但是还是挺快的,每秒千万-近亿次),赋值和访问效率很高,比原始指针慢没多少。几倍的代价貌似昂贵,其实没必要太担心。智能指针省心啊。满地优化的做法并不取。
    • qt的性能测试可以参考,具体执行次数,可以看看多少。
  • 裸指针和智能指针的性能对比 对比拷贝构造和std::move移动构造语义操作
    • 得到的结果是raw_ptr:unique_ptr:shared_ptr的性能是5:7:11,可见智能指针的效率还是相当诱人。
    • https://blog.csdn.net/s_xing/article/details/11559295?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-11559295-blog-49018673.pc_relevant_multi_platform_whitelistv4&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-11559295-blog-49018673.pc_relevant_multi_platform_whitelistv4&utm_relevant_index=1

使用指针的优点和缺点分析

  • 【QT小记】QT中智能指针的使用 QSharedPointer && QWeakPointer 有言简意赅的汇总了各个指针的特点

    • https://blog.csdn.net/lin786063594/article/details/124237989
  • 智能指针(shared_ptr)之使用场景、陷阱、性能分析、使用建议详述 作者对其他C++高级特性和其他指针也有说明分析

    • https://blog.csdn.net/weixin_44980842/article/details/121843881
    • 关于内存结构,智能指针的内存大小和裸指针区别
    • (2.1)尺寸(占据的内存)问题:
      shared_ptr的尺寸是裸指针的2倍weak_ptr尺寸也是裸指针的2倍。与weak_ptr一样,shared_ptr也是含有2个裸指针的。
      a)第一个裸指针指向的是这个智能指针所指向的对象
      b)第二个裸指针指向的是一个很大的数据结构–>控制块(由make_shared()函数来创建),这个控制块内含有:
      b.1)所指向对象的强引用计数
      b.2)所指向对象的弱引用计数
      b.3)其他data是:删除器指针、内存分配器等等。
  • 用裸指针来构造shared_ptr,裸指针和智能指针混用的时候,特别注意管理方式。

  • 强引用和弱引用智能指针的原理和区别

    • 弱引用:它仅仅是对象 存在时候的引用,当对象不存在时弱引用能够检测到,从而避免非法访问,弱引用也不会修改对象的引用计数。这意味这弱引用它并不对对象的内存进行管理,在功能上类似于普通指针,然而一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存
    • 强引用:内存管理和引用计数判断,具有对象的管理和调用权
    • https://blog.csdn.net/KingOfMyHeart/article/details/106988068
    • 总结就一句话: 弱智能智能在有需要的时候可以提升为强智能指针,通过判断返回值是否为nullptr查看是否提升成功;
  • weak_ptr ptr(t); // t为一个T对象

    • 则当t被销毁时,ptr 被自动置为无效。使用方法如下:
    • -if ( shard_ptr safePtr = ptr.lock() ) safePtr->Fun();
      可以看到,boost::weak_ptr必须从一个boost::share_ptr或另一个boost::weak_ptr转换而来,这也说明,进行该对象的内存管理的是那个强引用的boost::share_ptr。boost::weak_ptr只是提供了对管理对象的一个访问手段。boost::weak_ptr除了对所管理对象的基本访问功能(通过get()函数)外,还有两个常用的功能函数:expired()用于检测所管理的对象是否已经释放lock()用于获取所管理的对象的强引用指针。 如果lock返回的强引用指针为NULL空指针,说明该对象也已经被释放了。
      由于弱引用不更改引用计数,类似普通指针,只要把循环引用的一方使用弱引用,即可解除循环引用。对于上面的那个例子来说,只要把children的定义改为如下方式,即可解除循环引用:
    • https://blog.csdn.net/aikb6223/article/details/102349924?spm=1001.2101.3001.6650.7&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-7-102349924-blog-70217686.pc_relevant_multi_platform_whitelistv4&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-7-102349924-blog-70217686.pc_relevant_multi_platform_whitelistv4&utm_relevant_index=8
  • Android中的强弱引用计数(强弱指针)

    • 这里可以给一条规则:绝对不能在创建的RefBase对象还没有被一个确定的长作用域sp对象引用前,通过局部短作用域sp指针引用。
    • https://blog.csdn.net/simon_crystin/article/details/70217686
    • Bigclass(char *name, char * other){ ALOGD("start Construct another: %s,", mName); strcpy(mName, name); setWeakRefs(this); ALOGD("end Construct another: %s,", mName); }
      setWeakRefs(this); 在某个局部函数中构建了临时的智能指针管理对象,导致对象被析构,造成后面再使用该对象时候出现问题。
  • 这个就是上面某个地方指出的使用智能指针潜在的问题,裸指针和智能指针的不规范使用,导致裸指针指向的对象被释放回收了。然后传入到函数参数或者内部之前,必须也是智能指针类型的参数类型和对象。

  • 讲讲智能指针的缺点

  • https://blog.csdn.net/tiandyoin/article/details/79665274

    • 增加类型转换,混用和全部更换问题,“感染性问题”,使用注意场景问题,内存池更合适
    • 出个共享指针使用还导致循环引用,并不智能

智能指针线程安全问题

  • shared_ptr和weak_ptr都是线程安全的智能智能指针

其他有用的参考网址工具

  • 在线查看qt源代码的链接,链接如下:https://code.woboq.org/qt5/ 测试,需要访问google的服务,提供搜索查询。
  • qt单元测试,性能测试框架 https://blog.51cto.com/quantfabric/2114179
  • Boost里面所有的智能指针总结 https://blog.csdn.net/baidu_38749983/article/details/105623227
  • C++智能指针漫谈【下】 Boost和Loki的SmartPtr https://www.daimajiaoliu.com/daima/4e4269c0c900418
  • Hands-On Design Patterns With C++(十六)基于策略的设计(一) https://zhuanlan.zhihu.com/p/194365577

学习过程中遇到的问题,犯得错误点

  • error c2243:“类型转换” 转换存在,但无法访问

    • 如果派生类不是公有继承,那么用基类指针、引用指向该派生类对象时会发生什么?派生类内的成员的访问权限会不会发生什么变化?
    • 系统不支持此类类型转换,需要注意的是基类指针、引用不能指向继承方式为protected与private的派生类对象。
    • class LPerson : public QObject, 忘记写public继承了,默认为private继承,导致使用智能指针时候,访问派生类的对象失败。
    • https://blog.csdn.net/szlcw1/article/details/38555835 参考 https://www.cnblogs.com/mengwang024/p/4464212.html
    • 补充解释:
      • 有派生,原来的公有成员与保护成员,在派生类中均为私有的。那么使用基类指针或引用,能不能调用私有派生类继承过来的(在基类中原来是公有成员)私有成员?显然不能呀(破坏了类的封装性)
  • 单例的智能指针实现 https://blog.csdn.net/Andy_93/article/details/52779827

    • static QScopedPointer instance 创建的静态变量 instance 会在程序结束前析构(在main() 函数返回之后),调用 delete 删除它持有的 ConfigUtil 的指针对象
    • 和 QScopedPointer 的实现有关,QScopedPointer 被析构时不是直接在它的析构函数里调用 delete 删除它持有的指针对象,而是使用它的 Cleanup Handler 来删除,默认是 QScopedPointerDeleter,在 QScopedPointer 的帮助里有具体的说明
    • 本地编写上面程序测试,确实退出main函数之后,开始释放静态变量,调用静态变量的delete函数,来释放该对象,屏蔽qt的a.exec()事件循环调用,避免关闭窗口退出main,无法看到打印输出。可以看见打印消息: `ConfigUtil()
      “Verbose”
      ~ConfigUtil()
      Press to close this window…
  • 创建this的智能指针 https://blog.csdn.net/csn28/article/details/24790605 这有需要不?创建this的智能指针场景是啥?

  • 探讨容器类的对象创建和释放,https://www.bbsmax.com/A/GBJrBl3RJ0 ,测试了各种创建类型的创建和释放过程。本质还是生命周期的管理测试。

    • 总结,使用容器类存储对象时,最好使用对象指针类型,如:QList<TestObj*>,而不要使用 QList 这样的定义。建议采用 智能指针QSharedPointer 或 为对象设置parent 的方法来管理内存,避免内存泄露。
  • 这几个C++的坑,一旦踩中了,智能指针使用的效率问题1 https://www.bilibili.com/read/cv8273006

    • 尽量使用make_shared初始化 ,每个std::shared_ptr都指向一个控制块,控制块包含被指向对象的引用计数以及其他东西。
  • 智能指针的标准之争:Boost vs. Loki

    • Loki 是一种研究/概念验证类的东西。 Alexandrescu 提出新的想法,其他人则将这些想法用于现实世界。还有 boost::shared_ptr几乎字面意思是在 TR1 中。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值