当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《为什么QSharedPointer<T>::create调用不完整对象的析构函数?》经验,为你挑选了1个好方法。

我有以下代码示例:

#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.1MSVC2015+ Qt 5.6(从源代码构建)尝试了它.结果是一样的.



1> John Zwinck..:

看来您在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 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;
}

它对引用其他函数(主要在同一个文件中)有点复杂,但它似乎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::ExternalRefCountWithContiguousData 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跟踪器.希望附加一个工作补丁,他们会立即接受它.

推荐阅读
有风吹过best
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有