我有以下代码示例:
#include#include #include #include 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(); auto m2 = QSharedPointer::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::ExternalRefCountWithContiguousDataPrivate; 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; }
它对引用其他函数(主要在同一个文件中)有点复杂,但它似乎deleter
是在result
放置之前调用构造函数之前存储的new
.当你的构造函数抛出时,你的对象永远不会被完全构造,但QSharedPointer result
它已经被构造,并包含了删除器.从那里它是一个短暂的deleter
功能:
static void deleter(ExternalRefCountData *self) { ExternalRefCountWithContiguousData *that = static_cast(self); that->data.~T(); }
现在你的析构函数被调用,尽管你的构造函数从未完成.这是未定义的行为.如果你运气不好,这将破坏你的应用程序状态(因为它违反了只有在构造函数运行完成时才调用析构函数的规则 - 一些类类型可能依赖的规则).
一个可能的修复(我没有测试,但你可以)是:
static void noOpDeleter(ExternalRefCountData *self) { Q_UNUSED(self); } static inline QSharedPointer create() { typedef QtSharedPointer::ExternalRefCountWithContiguousDataPrivate; 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跟踪器.希望附加一个工作补丁,他们会立即接受它.