簡體   English   中英

為什么QSharedPointer <T> ::創建不完整對象的調用析構函數?

[英]Why does QSharedPointer<T>::create call destructor of incomplete object?

我有以下代碼示例:

#include <QCoreApplication>
#include <QSharedPointer>
#include <QDebug>

#include <memory>

class A
{
public:
    A()
    {
        throw 1;
    }
    ~A() { qDebug() << "A destr"; }
};

int main(int argc, char* argv[])
{
    QCoreApplication a(argc, argv);

    try
    {
        //auto m1 = std::make_shared<A>();
        auto m2 = QSharedPointer<A>::create();
    }
    catch (...)
    {
        qDebug() << "catch!";
    }

    return a.exec();
}

上面代碼的輸出是:

A destr
catch!

但是,如果我取消注釋與std::make_shared的行,則輸出如下:

catch!

那么為什么QSharedPointer::create不完整對象的調用析構函數呢? 那是一個錯誤還是我錯過了什么?

我使用MSVC2013 + Qt 5.5.1MSVC2015 + Qt 5.6 (從源構建)進行了嘗試。 結果是一樣的。

看來您在Qt中發現了一個錯誤。 我建議您提交一個錯誤報告並參考這個與之相關的錯誤: https : //bugreports.qt.io/browse/QTBUG-14637

問題似乎出在http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/tools/qsharedpointer_impl.h?h=v5.5.1#n420-其簡化代碼如下所示:

static inline QSharedPointer create()
{
    typedef QtSharedPointer::ExternalRefCountWithContiguousData<T> Private;
    typename Private::DestroyerFn destroy = &Private::deleter;

    QSharedPointer result(Qt::Uninitialized);
    result.d = Private::create(&result.value, destroy);

    new (result.data()) T();
    result.d->setQObjectShared(result.value, true);
    result.enableSharedFromThis(result.data());
    return result;
}

引用其他函數(大多數情況下是在同一文件中)有點復雜,但是似乎在將構造方法由放置new調用之前,將deleter存儲在result 當構造函數拋出異常時,就不會完全構造對象,但是QSharedPointer result已經構造QSharedPointer result ,並且包含刪除程序。 從那里到deleter功能deleter

static void deleter(ExternalRefCountData *self)
{
    ExternalRefCountWithContiguousData *that =
            static_cast<ExternalRefCountWithContiguousData *>(self);
    that->data.~T();
}

現在,即使您的構造函數從未完成,您的析構函數也會被調用。 那是未定義的行為。 如果您不走運,這會破壞您的應用程序狀態(因為這違反了只有在構造函數運行完成時才調用析構函數的規則(某些類類型可能依賴該規則)。

可能的解決方法(我尚未測試,但可以測試)是:

static void noOpDeleter(ExternalRefCountData *self)
{
    Q_UNUSED(self);
}

static inline QSharedPointer create()
{
    typedef QtSharedPointer::ExternalRefCountWithContiguousData<T> Private;
    typename Private::DestroyerFn noDestroy = &noOpDeleter;
    typename Private::DestroyerFn destroy = &Private::deleter;

    QSharedPointer result(Qt::Uninitialized);
    result.d = Private::create(&result.value, noDestroy);

    new (result.data()) T();
    result.d->destroyer = destroy;
    result.d->setQObjectShared(result.value, true);
    result.enableSharedFromThis(result.data());
    return result;
}

如果您可以驗證上述內容,則可以隨意將其編織成補丁程序並將其提交給Qt Bug跟蹤器。 希望附有一個有效的補丁程序,他們會及時接受它。

最后,我們將修復它! 我猜是Qt 5.8.2或Qt 5.9。

謝謝@JohnZwinck,您的想法很好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM