为什么 QSharedPointer:<T>:create 不完整对象的调用析构函数?
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
已经构造好了,并且包含了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跟踪器。希望有了工作补丁,他们会很快接受。
最后,我们将修复它!我猜应该是问题5.8.2或问题5.9。
谢谢@JohnZwinck,你的想法很好用。
- 什么时候调用组成单元对象的析构函数
- 使用基类指针创建对象时,缺少派生类析构函数
- 对具有动态分配的内存和析构函数的类对象的引用
- C++析构函数调用两次,堆栈分配的复合对象
- 如何从 Gtk::窗口调用派生对象的析构函数
- 为什么数组中对象的析构函数在被另一个对象替换时不被调用?
- 为什么为未删除的对象调用析构函数?
- 对象的构造函数和析构函数
- 循环中本地对象的析构函数是否保证在下一次迭代之前被调用?
- C++使用函数对象的线程,如何调用多个析构函数而不是构造函数?
- 从未调用shared_ptr对象的析构函数
- 是否可以在其析构函数中使用指向已销毁对象的指针?
- 使用私有析构函数删除动态分配的对象
- C++ std::线程调用方法,从对象原因到调用此类的析构函数
- 为什么在这里调用析构函数,以及在调用该对象析构函数后如何调用对象成员函数?
- 对象析构函数在多线程处理时不断被调用,但该对象并未超出范围
- C++包含包含指针的对象的对象析构函数
- Objective-C++C++对象析构函数
- 结构对象析构函数
- 在Exit()时调用基本对象析构函数