Qt智能指针

Posted QtHalcon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt智能指针相关的知识,希望对你有一定的参考价值。

QPointer

        QPointer属于Qt对象模型的特性,本质是一个模板类,它为QObje提供了guarded pointer。当其指向的对象(必须是QObject及其派生类)被销毁时,它会被自动置NULL,原理是其对象析构时会执行QObject的析构函数,进而执行QObjectPrivate::clearGuards(this);,这也是基于其指向对象都继承自QObject的原因。

        QPointer对QMetaObject的相关操作做了简单的封装,这里的基本思想是
在QPointer构造的时候调用QMetaObject::addGuard(&o),把T的指针加入QMetaObject内的一个哈希表中,
        在QPointer析构的时候调用QMetaObject::removeGuard(&o),把T的指针从哈希表中删除。

QPointer<Test> t = new Test();      // Test类必须继承QObject
delete t;   //对象被delete之后,t会自动置NULL,而不会成为悬挂(dangling)的野指针
if(t.isNull())
    qDebug()<<"NULL";

运行后会输出NULL。

实际中,QPointer用于解决这样的问题:在其他地方都用到了某个指针,在这个指针的对象被delete后,将指针置为空,那么其他地方的指针会变为野指针。也就是在C++中有这样的代码:

Test* p1 = new Test();
Test* p2 = p1;
delete p1;
p1 = NULL;
if(t2)
    qDebug()<<"t2不是NULL";
else
    qDebug()<<"t2成为NULL";

运行结果是t2不是NULL,也就是说t2成为了野指针。

有了QPointer,可以这样解决这个问题:

Test* t1 = new Test();
QPointer<Test> t2 = t1;
delete t1;
t1 = NULL;
if(t2)
    qDebug()<<"t2不是NULL";
else
    qDebug()<<"t2成为NULL";

运行结果是t2成为NULL,t2不再是野指针了。当然这里的t1最好也用QPointer,不过重点是t2。

QScopedPointer

        QScopedPointer类保存了一个指针,指向在堆上分配的对象,在对象销毁时delete这个指针。从scope这个词就可以知道对象指针在出了作用域后就会被delete掉,不必手动delete。这个智能指针只能在本作用域里使用,不希望被转让。因为它的拷贝构造和赋值操作都是私有的,与QObject及其派生类风格相同。

Test* p = new Test();
QScopedPointer<Test> p2(p);
p2.data()->foo();
p2.take()->foo();
if(p2 == NULL)
    qDebug()<<"p2 is null";
else
    qDebug()<<"p2 is not null";

运行结果是:p2==NULL

        T *QScopedPointer::data() const返回指向对象的常量指针,QScopedPointer仍拥有对象所有权。
        T *QScopedPointer::take()也是返回对象指针,但QScopedPointer不再拥有对象所有权,而是转移到调用这个函数的caller,同时QScopePointer对象指针置为NULL。注意:如果没有使用take,p2会成为野指针。
        void QScopedPointer::reset(T *other = Q_NULLPTR):delete目前指向的对象,调用其析构函数,将指针指向另一个对象other,所有权转移到other。

        有些运算符是不支持的,如赋值运算符:

QScopedPointer<int> i(new int(42));
i = new int(43); // 编译不通过
i.reset(new int(43)); // 正确

        以上代码仅仅是用于处理new的情况,不能用于malloc和new []数组。

        经常用于这样的代码风格:

class MyPrivateClass; // forward declare MyPrivateClass
class MyClass

private:
  QScopedPointer<MyPrivateClass> privatePtr; // QScopedPointer to forward declared class
public:
  MyClass(); // OK
  inline ~MyClass()  // VIOLATION - Destructor must not be inline
private:
  Q_DISABLE_COPY(MyClass) // OK - copy constructor and assignment operators
                           // are now disabled, so the compiler won't implicitely generate them.
;

        在Qt源码中,经常用于D指针,例如在qpainter.h中,有代码: QScopedPointer<QPainterPrivate> d_ptr,以后研究D指针时再深入探讨

QSharedDataPointer

        更像普通的指针,也是用于堆上分配的对象,但它是一个计数型只能指针,可以自由拷贝和赋值,可以共享,当此对象被一个QSharedPointer指针指向时,计数加1,少一个QSharedPointer指针指向时,计数减1,一直到计数为0时,对象才会销毁。

        同QScopePointer类似,QSharedPointer也会在离开作用域后删除指针。
        QSharedPointer和QWeakPointer都是线程安全的,可以原子地操作指针,不同线程可以获取这两种指针指向的对象而不必加锁。

        void QSharedPointer::clear():清除所指向的对象,如果是最后一个指向,那么指针会被delete。
        data函数,isNull函数同QScopePointer功能一样。

    Test* p = new Test();
    QSharedPointer<Test> p1(p);
    QSharedPointer<Test> p2(p1);
    QSharedPointer<Test> p3(p1);
    p1.clear();
    p2.clear();
    p3.clear();
    qDebug()<<"3333333";
    if(p1.isNull())
        qDebug()<<"p1 is null";
    else
        qDebug()<<"p1 is not null";
    return a.exec();

        在Qt main函数中测试会遇到事件循环的问题,就是运行GUI程序的return a.exec()实际进入事件循环,没有离开作用域,这种情况下想销毁对象就需要所有指针都clear。上面的代码中,指针计数为3,所以必须三个指针都执行clear,然后才会销毁对象,即调用析构函数,然后三个指针都成为NULL。有一个指针没有clear就不会销毁对象。

        假如不进事件循环,而是return 0,那么就是按作用域机制,不需要所有指针都clear也会销毁对象。

以上是关于Qt智能指针的主要内容,如果未能解决你的问题,请参考以下文章

智能指针std::shared_ptr初始化时可能泄露的地方

C++野指针 Qt报错received a signal, 大佬帮我看下这段哪里会出问题,所有变量没写的在其他地方声明了

Qt 智能指针学习(7种QT的特有指针)

Qt 智能指针学习

Qt6STL-QVector循环删除指针浅谈

智能指针