[英]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.1
和MSVC2015
+ 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.