代码中出现一个bug,最终发现是由于在某个特殊情况下出现了使用垂悬指针,造成了程序崩溃,进而学习了解了Qt的智能指针机制。
一、悬垂指针的问题
如图,有两个指针a和b指向同一片内存,如果删除其中一个指针a,再去使用指针b的话,程序会崩溃。因为指针b此时已经是一个垂悬指针(Dangling pointer)了,它指向的内存已经被释放不再有效。
使用指针b之前先判断b是否为空,这个做法在这里是不起作用的。问题的本质是通过指针a去释放内存时,指针b没有同步地置为空。
假如指针b能随内存的释放而自动置为空就好了,这正是智能指针所要解决的问题。
二、Qt中的智能指针
Qt提供了若干种智能指针:QPointer、QSharedPointer、QWeakPointer、QScopedPointer、QScopedArrayPointer、QSharedDataPointer、QExplicitlySharedDataPointer。
注:1、笔者Qt版本为4.8; 2、下述示例代码中"Plot"为"QObject"类的子类。
1、QPointer
QPointer只用于QObject的实例。如果它指向的对象被销毁,它将自动置空。如图:
这是Qt体系下的专门用于QObject的智能指针。常见使用方法:
QPointer<Plot> a(new T()); //构造
QPointer<Plot> a(b); //构造
a.isNull(); //判空
a.data(); //返回裸指针
2、QSharedPointer & QWeakPointer
QSharedPointer是引用计数(强)指针,当所有强指针销毁时,实际对象才会销毁。
QWeakPointer是弱指针,可以持有对QSharedPointer的弱引用。它作为一个观察者,不会引起实际对象销毁,当对象销毁时会自动置空。
这两种指针同时都有以下3个成员:强引用计数strongRef,弱引用计数weakRef和数据data。
两种指针分别对应于C++中的std::shared_ptr和std::weak_ptr。常见使用方法:
//构造
QSharedPointer<Plot> a(new Plot());
QSharedPointer<Plot> b = a;
QWeakPointer<Plot> c = a; //强指针构造弱指针
QWeakPointer<Plot> d(a);
//使用
c.clear(); //清除
a.isNull(); //判空
a->func(...); //(按常规指针来使用 "->")
QSharedPointer<Plot> e = d.toStrongRef(); //弱指针转为强指针。注意,弱指针无法操纵数据,必须转为强指针
QWeakPointer<Plot> f = e.toWeakRef();//强指针显式转为弱指针
QSharedPointer<Plot> g = e.dynamicCast<T>(); //动态类型转换
3、QScopedPointer
QScopedPointer保证当当前范围消失时指向的对象将被删除。它拥有一个很好的名字,它向代码的阅读者传递了明确的信息:这个智能指针只能在本作用域里使用,不希望被转让,因为它的拷贝构造和赋值操作都是私有的。
相当于C++中的std::unique_ptr,实例代码:
func(){
Plot* plot = new Plot();
//QScopedPointer出作用域自动销毁内存
QScopedPointer<Plot>qsp(plot);
//plot没有内存泄漏