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ù)的形式,進行對象跟蹤。