Qt之QPointer

QPointer對象的實現(xiàn)如下,其中我們可以看到,QPointer在構造時,模板類型不能是指針(通過c++的偏特化特性來判斷是否傳入類型為指針)。其次是對象的成員QWeakPointer,QPointer是對QWeakPointer進行封裝,使得QWeakPointer比較簡單易用。因此,在探尋QPointer為啥能夠在QObject析構時,自動置為nullptr,其實底層原理實際在QWeakPointer中實現(xiàn)。

class QPointer
{
    Q_STATIC_ASSERT_X(!std::is_pointer<T>::value, "QPointer's template type must not be a pointer type");
...
    QWeakPointer<QObjectType> wp;
public:
    inline QPointer() { }
    inline QPointer(T *p) : wp(p, true) { }
...
};

QPointer構造一個指針對象時,QWeakPointer調(diào)用的構造函數(shù)為

inline QWeakPointer(X *ptr, bool) : d(ptr ? Data::getAndRef(ptr) : nullptr), value(ptr)

Data::getAndRef的具體實現(xiàn)如下,獲取ptr對象的引用計算,返回弱引用指針:

QtSharedPointer::ExternalRefCountData *QtSharedPointer::ExternalRefCountData::getAndRef(const QObject *obj)
{
    Q_ASSERT(obj);
    QObjectPrivate *d = QObjectPrivate::get(const_cast<QObject *>(obj));
    Q_ASSERT_X(!d->wasDeleted, "QWeakPointer", "Detected QWeakPointer creation in a QObject being deleted");

    ExternalRefCountData *that = d->sharedRefcount.load();
    if (that) {
        that->weakref.ref();
        return that;
    }

    // we can create the refcount data because it doesn't exist
    ExternalRefCountData *x = new ExternalRefCountData(Qt::Uninitialized);
    x->strongref.store(-1);
    x->weakref.store(2);  // the QWeakPointer that called us plus the QObject itself

    ExternalRefCountData *ret;
    if (d->sharedRefcount.testAndSetOrdered(nullptr, x, ret)) {     // ought to be release+acquire; this is acq_rel+acquire
        ret = x;
    } else {
        // ~ExternalRefCountData has a Q_ASSERT, so we use this trick to
        // only execute this if Q_ASSERTs are enabled
        Q_ASSERT((x->weakref.store(0), true));
        delete x;
        ret->weakref.ref();
    }
    return ret;
}

QWeakPointer的默認構造會自動置nullptr,因此,使用QPointer作為成員變量時,不需要額外初始化為nullptr

inline QPointer() { }
inline QWeakPointer() Q_DECL_NOTHROW : d(nullptr), value(nullptr) { }

QSharedPointer中的引用計數(shù)對象也是ExternalRefCountData,QObjectPrivate中的ExternalRefCountData和QSharedPointer中記錄對象的ExternalRefCountData有一些區(qū)別,盡管它們的用途和機制都與引用計數(shù)有關。

1. QObjectPrivate::ExternalRefCountData

QObjectPrivate 中的 ExternalRefCountData 是 QObject 用來管理其外部引用計數(shù)的結構。QObject 對象的外部引用計數(shù)用于管理對象的生命周期,特別是在對象被外部引用時防止其被過早刪除。這個引用計數(shù)主要與事件循環(huán)、信號槽機制以及一些特殊的QObject派生類相關聯(lián)。
QObjectPrivate 中的 ExternalRefCountData 通過引用計數(shù)跟蹤外部對該 QObject 的引用。當引用計數(shù)為0時,QObject 可以安全地銷毀。
這部分機制主要是在Qt的內(nèi)存管理和QObject生命周期管理中起作用。

2. QSharedPointer::ExternalRefCountData

QSharedPointer 中的 ExternalRefCountData 是智能指針機制的一部分,用于管理指針的引用計數(shù)。QSharedPointer 是一個智能指針類,提供了自動的內(nèi)存管理,通過引用計數(shù)來決定對象的刪除時間。
當多個 QSharedPointer 指向同一對象時,這個對象的引用計數(shù)會增加。只有當最后一個 QSharedPointer 被銷毀或者重置時,指向的對象才會被刪除。
QSharedPointer::ExternalRefCountData 管理的引用計數(shù)不僅僅是對象本身的引用,還包括指向同一對象的所有 QSharedPointer 實例的引用。

QWeakPointer獲取所指對象QObjectPrivate的弱引用,對象指針獲取和判空時,都會 d->strongref.load()判斷所指對象的強引用計數(shù),通過強引用計數(shù)來判斷對象是否還存在。

 bool isNull() const Q_DECL_NOTHROW { return d == nullptr || d->strongref.load() == 0 || value == nullptr; }
T *data() const Q_DECL_NOTHROW { return d == nullptr || d->strongref.load() == 0 ? nullptr : value; }

那么,QObjectPrivate中的sharedRefcount是怎么維護的呢?
在QObject::~QObject()中,會獲取QObjectPrivate中的sharedRefcount,并且設置 sharedRefcount->strongref.store(0)為0,QWeakPointers在跟蹤這個對象時,會通過判斷strongref為0時來確認該對象是否被銷毀,sharedRefcount的實際銷毀時機在最后一個weakref.deref()時。

    QtSharedPointer::ExternalRefCountData *sharedRefcount = d->sharedRefcount.load();
    if (sharedRefcount) {
        if (sharedRefcount->strongref.load() > 0) {
            qWarning("QObject: shared QObject was deleted directly. The program is malformed and may crash.");
            // but continue deleting, it's too late to stop anyway
        }

        // indicate to all QWeakPointers that this QObject has now been deleted
        sharedRefcount->strongref.store(0);
        if (!sharedRefcount->weakref.deref())
            delete sharedRefcount;
    }
inline ~QWeakPointer() { if (d && !d->weakref.deref()) delete d; }

因此,sharedRefcount->strongref.store(0),是QWeakPointer判斷所指對象是否被銷毀的依據(jù),QObjectPrivate的ExternalRefCountData 在Data::getAndRef時被初始化,在QObject::~QObject()時,sharedRefcount->strongref.store(0),標記對象被銷毀。通過引用計數(shù)的形式,進行對象跟蹤。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容