Qt之QPointer

QPointer對象的實(shí)現(xiàn)如下,其中我們可以看到榨乎,QPointer在構(gòu)造時(shí)创译,模板類型不能是指針(通過c++的偏特化特性來判斷是否傳入類型為指針)。其次是對象的成員QWeakPointer叁熔,QPointer是對QWeakPointer進(jìn)行封裝,使得QWeakPointer比較簡單易用床牧。因此荣回,在探尋QPointer為啥能夠在QObject析構(gòu)時(shí),自動置為nullptr戈咳,其實(shí)底層原理實(shí)際在QWeakPointer中實(shí)現(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構(gòu)造一個(gè)指針對象時(shí),QWeakPointer調(diào)用的構(gòu)造函數(shù)為

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

Data::getAndRef的具體實(shí)現(xiàn)如下著蛙,獲取ptr對象的引用計(jì)算删铃,返回弱引用指針:

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的默認(rèn)構(gòu)造會自動置nullptr,因此踏堡,使用QPointer作為成員變量時(shí)猎唁,不需要額外初始化為nullptr

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

QSharedPointer中的引用計(jì)數(shù)對象也是ExternalRefCountData,QObjectPrivate中的ExternalRefCountData和QSharedPointer中記錄對象的ExternalRefCountData有一些區(qū)別顷蟆,盡管它們的用途和機(jī)制都與引用計(jì)數(shù)有關(guān)诫隅。

1. QObjectPrivate::ExternalRefCountData

QObjectPrivate 中的 ExternalRefCountData 是 QObject 用來管理其外部引用計(jì)數(shù)的結(jié)構(gòu)缎患。QObject 對象的外部引用計(jì)數(shù)用于管理對象的生命周期,特別是在對象被外部引用時(shí)防止其被過早刪除阎肝。這個(gè)引用計(jì)數(shù)主要與事件循環(huán)挤渔、信號槽機(jī)制以及一些特殊的QObject派生類相關(guān)聯(lián)。
QObjectPrivate 中的 ExternalRefCountData 通過引用計(jì)數(shù)跟蹤外部對該 QObject 的引用风题。當(dāng)引用計(jì)數(shù)為0時(shí)判导,QObject 可以安全地銷毀。
這部分機(jī)制主要是在Qt的內(nèi)存管理和QObject生命周期管理中起作用沛硅。

2. QSharedPointer::ExternalRefCountData

QSharedPointer 中的 ExternalRefCountData 是智能指針機(jī)制的一部分眼刃,用于管理指針的引用計(jì)數(shù)。QSharedPointer 是一個(gè)智能指針類摇肌,提供了自動的內(nèi)存管理擂红,通過引用計(jì)數(shù)來決定對象的刪除時(shí)間。
當(dāng)多個(gè) QSharedPointer 指向同一對象時(shí)围小,這個(gè)對象的引用計(jì)數(shù)會增加昵骤。只有當(dāng)最后一個(gè) QSharedPointer 被銷毀或者重置時(shí),指向的對象才會被刪除肯适。
QSharedPointer::ExternalRefCountData 管理的引用計(jì)數(shù)不僅僅是對象本身的引用变秦,還包括指向同一對象的所有 QSharedPointer 實(shí)例的引用。

QWeakPointer獲取所指對象QObjectPrivate的弱引用框舔,對象指針獲取和判空時(shí)蹦玫,都會 d->strongref.load()判斷所指對象的強(qiáng)引用計(jì)數(shù),通過強(qiáng)引用計(jì)數(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是怎么維護(hù)的呢?
在QObject::~QObject()中纬凤,會獲取QObjectPrivate中的sharedRefcount福贞,并且設(shè)置 sharedRefcount->strongref.store(0)為0,QWeakPointers在跟蹤這個(gè)對象時(shí)移斩,會通過判斷strongref為0時(shí)來確認(rèn)該對象是否被銷毀肚医,sharedRefcount的實(shí)際銷毀時(shí)機(jī)在最后一個(gè)weakref.deref()時(shí)绢馍。

    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時(shí)被初始化猖任,在QObject::~QObject()時(shí),sharedRefcount->strongref.store(0)瓷耙,標(biāo)記對象被銷毀朱躺。通過引用計(jì)數(shù)的形式刁赖,進(jìn)行對象跟蹤。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末长搀,一起剝皮案震驚了整個(gè)濱河市宇弛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌源请,老刑警劉巖枪芒,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谁尸,居然都是意外死亡舅踪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門良蛮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抽碌,“玉大人,你說我怎么就攤上這事决瞳』踽悖” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵皮胡,是天一觀的道長破婆。 經(jīng)常有香客問我,道長胸囱,這世上最難降的妖魔是什么祷舀? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮烹笔,結(jié)果婚禮上裳扯,老公的妹妹穿的比我還像新娘。我一直安慰自己谤职,他們只是感情好饰豺,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著允蜈,像睡著了一般冤吨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饶套,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天漩蟆,我揣著相機(jī)與錄音,去河邊找鬼妓蛮。 笑死怠李,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捺癞,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼夷蚊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了髓介?” 一聲冷哼從身側(cè)響起惕鼓,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎唐础,沒想到半個(gè)月后呜笑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡彻犁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年叫胁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汞幢。...
    茶點(diǎn)故事閱讀 38,724評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驼鹅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出森篷,到底是詐尸還是另有隱情输钩,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布仲智,位于F島的核電站买乃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏钓辆。R本人自食惡果不足惜剪验,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望前联。 院中可真熱鬧功戚,春花似錦、人聲如沸似嗤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烁落。三九已至乘粒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伤塌,已是汗流浹背灯萍。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寸谜,地道東北人竟稳。 一個(gè)月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像熊痴,于是被迫代替她去往敵國和親他爸。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容