简体   繁体   English

为什么QSharedPointer <T> ::创建不完整对象的调用析构函数?

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

I have following code example: 我有以下代码示例:

#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();
}

The output for the above code is: 上面代码的输出是:

A destr
catch!

But if I uncomment the line with std::make_shared the output is following: 但是,如果我取消注释与std::make_shared的行,则输出如下:

catch!

So why does QSharedPointer::create call destructor of incomplete object? 那么为什么QSharedPointer::create不完整对象的调用析构函数呢? Is that a bug or I'm missing something? 那是一个错误还是我错过了什么?

I tried it with MSVC2013 + Qt 5.5.1 and MSVC2015 + Qt 5.6 (built from sources). 我使用MSVC2013 + Qt 5.5.1MSVC2015 + Qt 5.6 (从源构建)进行了尝试。 The result is the same. 结果是一样的。

It appears you have found a bug in Qt. 看来您在Qt中发现了一个错误。 I suggest you file a bug report and reference this somewhat related bug: https://bugreports.qt.io/browse/QTBUG-14637 我建议您提交一个错误报告并参考这个与之相关的错误: https : //bugreports.qt.io/browse/QTBUG-14637

The problem seems to be in http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/tools/qsharedpointer_impl.h?h=v5.5.1#n420 - whose simplified code looks like this: 问题似乎出在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;
}

It's a little complicated with references to other functions (mostly in the same file), but it appears that deleter is stored in result before the constructor is called by placement new . 引用其他函数(大多数情况下是在同一文件中)有点复杂,但是似乎在将构造方法由放置new调用之前,将deleter存储在result When your constructor throws, your object is never completely constructed, but the QSharedPointer result is constructed already, and contains the deleter. 当构造函数抛出异常时,就不会完全构造对象,但是QSharedPointer result已经构造QSharedPointer result ,并且包含删除程序。 From there it's a short hop to the deleter function: 从那里到deleter功能deleter

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

And now your destructor is called, despite your constructor never having completed. 现在,即使您的构造函数从未完成,您的析构函数也会被调用。 That's undefined behavior. 那是未定义的行为。 If you're unlucky, this will corrupt your application state (because it goes against the rule that a destructor is only called if a constructor runs to completion--a rule some class types may rely on). 如果您不走运,这会破坏您的应用程序状态(因为这违反了只有在构造函数运行完成时才调用析构函数的规则(某些类类型可能依赖该规则)。

A possible fix (which I haven't tested, but you can) is: 可能的解决方法(我尚未测试,但可以测试)是:

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;
}

If you can validate the above, you should feel free to weave it into a patch and submit it to the Qt bug tracker. 如果您可以验证上述内容,则可以随意将其编织成补丁程序并将其提交给Qt Bug跟踪器。 Hopefully with a working patch attached they'll accept it promptly. 希望附有一个有效的补丁程序,他们会及时接受它。

Finally, we will have it fixed ! 最后,我们将修复它! I gues it would be Qt 5.8.2 or Qt 5.9. 我猜是Qt 5.8.2或Qt 5.9。

Thanks @JohnZwinck, your idea works just fine. 谢谢@JohnZwinck,您的想法很好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM