繁体   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