Qt智能指针折腾死我了,几天人参的教训

彩阳 2013-03-26 10:47:47
本来自作聪明使用了智能指针,每想到碰到了QObject,结果人参就这么没了……
作为教训,写了一篇博客
博客内容:
Qt的智能指针是在Qt4.5的时候提出来的,目的是为了让Qt应用程序能够摆脱硬编码delete的问题,避免的内存泄漏。但是在我编写程序的时候,发现它和QObject对象树系统(父子系统)相结合屡屡出错。这里以日志的形式记录下来,防止别人再出错。
Qt的智能指针主要有QSharedPointer和QScopedPointer,当然还有其它的类,但是用得较少。QSharedPointer是在Qt4.5的时候引进的,而QScopedPointer是在Qt4.6的时候引进的,我想这是因为Qt开发者看到了Boost或是Loki在智能指针这方面的卓越研究,而决定自己制作一套强大的智能指针吧。Qt的智能指针风格类似Boost,为每种指针提供了一个从字面上很好理解的类来进行封装。但是开发者(包括我)需要注意的是,它包含的对象若是从QObject继承下来的子类,那么就要担心了。在运行的时候一旦智能指针销毁,就会出错,让人摸不着头脑。
原因在于,QObject自有对象树系统(父子系统),它在和其它QObject子类进行交互的时候会将对方的指针保存起来,形成父子关系,最终一个QObject子类指针会形成一个强大的树状结构,当父亲销毁的时候,会先销毁它的孩子(如果它的孩子是通过new操作符在堆上创建的话)。但是智能指针在保有QObject子类的时候会自动调用它的析构函数,从而引起事实上的两次delete,这个时候编译器的就会报错。
那我熟悉的QScopedPointer来说,本来将它用在类的成员中是一个很好的选择,但是由于它保有的是QObject的子类,这个智能指针在和其它QObject子类交互的时候难免会被对方保有原始指针的值,在进入类的析构函数,QScopedPointer保有原始指针的值会被先于释放并置为“已删除”的值0xfeeefeee,这个时候再通过智能指针的自动清理只可能会带来运行错误。在qscopedpointer.h源码中,我们看到QScopedPointerDeleter类的cleanup静态函数并不带有在delete之前的指针的值检测,于是在delete一个无效的指针时,错误发生了。
...全文
5243 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
cdpcsc 2015-11-26
  • 打赏
  • 举报
回复
初看很有道理,经验证,楼主结论是错误的。 不论QScopedPointer 还是std::unique_ptr,指向一个创建时指定Parent的对象时,Parent销毁时,其都会被正常释放。
blueice12 2014-09-16
  • 打赏
  • 举报
回复 2
楼上说的判断0xfeeefeee肯定是不行的,因为delete后清成feee这是VC的运行库在debug版本下的行为,在release版本中不会这么做,另外,使用QT的程序都是跨平台的,到了其他平台肯定也不是这样的。 只有避免两次释放才是解决问题的正途。这个问题的原因是在QT的对象树系统中,删除父节点也会删除子节点,但是因为它的对象树中的对象并不是使用QSharedPointer管理的,而是使用的裸指针,这样你的QSharedPointer并不会得到通知而变成悬空指针。 建议你对于放在对象树中的QObject对象,使用QPointer指针来保管,这样,既不会影响对象的正常释放,也可以在QObject释放时得到通知自动变成NULL,以后就可以判断是不是NULL来避免错误操作。
Maitianshouwei 2014-06-15
  • 打赏
  • 举报
回复
lz,我现在也遇到了类似的问题,使用qsharedpointer和qobject时莫名出现violation reading location 0xFFFFFFFFFFFFFFFF。请问楼主,我该怎么解决这个问题,工程量太大,不想更换数据结构了
十八道胡同 2013-03-27
  • 打赏
  • 举报
回复
我看了下QGraphicsScene ,QGraphicsView ,他们addItem/addWidget的时候都是普通的指针类型,你如果定义一个智能指针来用,会出类型不匹配的编译错误。 所以我也就放弃使用智能指针了。
彩阳 2013-03-27
  • 打赏
  • 举报
回复
目前得出了一个结论:得出一个结论,只要加入了QObject对象树系统(父子机制),那么内存管理不是你的事儿了,你也不应该管,也不应该让智能指针管。
彩阳 2013-03-27
  • 打赏
  • 举报
回复
引用 2 楼 fsx92 的回复:
不错不错,不过既然被qt系统删除的指针的值置为0xfeeefeee,那么为什么不在智能指针的时候先判断指针的值,再进行删除呢。
哦,上述方法也会出现运行错误,我昨天尝试过了。主要是指针是正确的,但是虚指针vptr是0xfeeefeee,它指向的是QObject,这就更难解决了。我没有办法才放弃了QScopedPointer。
彩阳 2013-03-27
  • 打赏
  • 举报
回复
引用 2 楼 fsx92 的回复:
不错不错,不过既然被qt系统删除的指针的值置为0xfeeefeee,那么为什么不在智能指针的时候先判断指针的值,再进行删除呢。
问得好!我尝试了,首先让一个指针和一个十六进制的数相比较,会出现编译错误; 然后,可以通过方法让其通过编译,我定义了一个这样的宏: #define SAFE_DELETE( p ) if ( p != 0 && (unsigned long)p != 0xfeeefeee ) delete p, p = 0; 不过不知道在任何编译器下,任何平台下delete指针后,指针的值都会置0xfeeefeee,因为Qt应用程序需要跨平台。目前在MSDN上介绍说delete一个指针后会置指针为0xfeeefeee。求更厉害的高手给出肯定的答复。
嵌入式农民工 2013-03-27
  • 打赏
  • 举报
回复
不错不错,不过既然被qt系统删除的指针的值置为0xfeeefeee,那么为什么不在智能指针的时候先判断指针的值,再进行删除呢。
乔巴好萌 2013-03-27
  • 打赏
  • 举报
回复
学习学习

16,201

社区成员

发帖
与我相关
我的任务
社区描述
Qt 是一个跨平台应用程序框架。通过使用 Qt,您可以一次性开发应用程序和用户界面,然后将其部署到多个桌面和嵌入式操作系统,而无需重复编写源代码。
社区管理员
  • Qt
  • 亭台六七座
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧