Pimpl + QSharedPointer语言 - Destructor = Disaster
Pimpl + QSharedPointer - Destructor = Disaster
昨天我遇到了痛苦,这让我沮丧了24小时。问题归结为在随机基础上发生的意外崩溃。更复杂的是,调试报告也有绝对的随机模式。更复杂的是,所有的调试跟踪都指向随机的 Qt源代码或本机dll,也就是说,每次都证明问题不是在我这边。
这里有一些这样可爱的报告的例子:
Program received signal SIGSEGV, Segmentation fault.
0x0000000077864324 in ntdll!RtlAppendStringToString () from C:Windowssystem32ntdll.dll
(gdb) bt
#0 0x0000000077864324 in ntdll!RtlAppendStringToString () from C:Windowssystem32ntdll.dll
#1 0x000000002efc0230 in ?? ()
#2 0x0000000002070005 in ?? ()
#3 0x000000002efc0000 in ?? ()
#4 0x000000007787969f in ntdll!RtlIsValidHandle () from C:Windowssystem32ntdll.dll
#5 0x0000000000000000 in ?? ()
warning: HEAP: Free Heap block 307e5950 modified at 307e59c0 after it was freed
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00000000778bf0b2 in ntdll!ExpInterlockedPopEntrySListFault16 () from C:Windowssystem32ntdll.dll
(gdb) bt
#0 0x00000000778bf0b2 in ntdll!ExpInterlockedPopEntrySListFault16 () from C:Windowssystem32ntdll.dll
#1 0x000000007786fd34 in ntdll!RtlIsValidHandle () from C:Windowssystem32ntdll.dll
#2 0x0000000077910d20 in ntdll!RtlGetLastNtStatus () from C:Windowssystem32ntdll.dll
#3 0x00000000307e5950 in ?? ()
#4 0x00000000307e59c0 in ?? ()
#5 0x00000000ffffffff in ?? ()
#6 0x0000000000220f10 in ?? ()
#7 0x0000000077712d60 in WaitForMultipleObjectsEx () from C:Windowssystem32kernel32.dll
#8 0x0000000000000000 in ?? ()
Program received signal SIGSEGV, Segmentation fault.
0x0000000000a9678a in QBasicAtomicInt::ref (this=0x8) at ../../include/QtCore/../../../qt-src/src/corelib/arch/qatomic_x86_64.h:121
121 : "memory");
(gdb) bt
#0 0x0000000000a9678a in QBasicAtomicInt::ref (this=0x8) at ../../include/QtCore/../../../qt-src/src/corelib/arch/qatomic_x86_64.h:121
#1 0x00000000009df08e in QVariant::QVariant (this=0x21e4d0, p=...) at d:/Distributions/qt-src/src/corelib/kernel/qvariant.cpp:1426
#2 0x0000000000b4dde9 in QList<QVariant>::value (this=0x323bd480, i=1) at ../../include/QtCore/../../../qt-src/src/corelib/tools/qlist.h:666
#3 0x00000000009ccff7 in QObject::property (this=0x3067e900,
name=0xa9d042a <QCDEStyle::drawPrimitive(QStyle::PrimitiveElement, QStyleOption const*, QPainter*, QWidget const*) const::pts5+650> "_q_stylerect")
at d:/Distributions/qt-src/src/corelib/kernel/qobject.cpp:3742
#4 0x0000000000000000 in ?? ()
正如你所看到的,这个东西是非常讨厌的,它没有提供任何有用的信息。但是,有一件事我没有注意到。这是编译过程中一个奇怪的警告,也很难用眼睛捕捉到:
In file included from d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/qsharedpointer.h:50:0,
from d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/QSharedPointer:1,
from ../../../../source/libraries/Project/sources/Method.hpp:4,
from ../../../../source/libraries/Project/sources/Slot.hpp:4,
from ../../../../source/libraries/Project/sources/Slot.cpp:1:
d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/qsharedpointer_impl.h: In instantiation of 'static void QtSharedPointer::ExternalRefCount<T>::deref(QtSharedPointer::ExternalRefCount<T>::Data*, T*) [with T = Project::Method::Private; QtSharedPointer::ExternalRefCount<T>::Data = QtSharedPointer::ExternalRefCountData]':
d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/qsharedpointer_impl.h:336:11: required from 'void QtSharedPointer::ExternalRefCount<T>::deref() [with T = Project::Method::Private]'
d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/qsharedpointer_impl.h:401:38: required from 'QtSharedPointer::ExternalRefCount<T>::~ExternalRefCount() [with T = Project::Method::Private]'
d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/qsharedpointer_impl.h:466:7: required from here
d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/qsharedpointer_impl.h:342:21: warning: possible problem detected in invocation of delete operator: [enabled by default]
d:/Libraries/x64/MinGW-w64/4.7.2/Qt/4.8.4/include/QtCore/qsharedpointer_impl.h:337:28: warning: 'value' has incomplete type [enabled by default]
实际上,我把这个警告只是作为最后的手段,因为在如此绝望地寻找错误时,代码已经感染了日志死亡。
在仔细阅读了之后,我回忆起,例如,如果对Pimpl使用std::unique_ptr
或std::scoped_ptr
,则必须提供析构函数,否则代码甚至无法编译。然而,我也记得std::shared_ptr
并不关心析构函数,没有它也可以工作得很好。这是我没有注意到这个奇怪警告的另一个原因。长话短说,当我添加析构函数时,这种随机崩溃就停止了。看起来Qt的QSharedPointer
与std::shared_ptr
相比有一些设计缺陷。我想如果Qt开发人员将这个警告转换为错误会更好,因为像这样的调试马拉松根本不值得一个人的时间,精力和神经。
我的问题是:
-
QSharedPointer
怎么了?为什么析构函数如此重要? 为什么没有析构函数就会崩溃?这些对象(使用Pimpl + - 还有其他陷阱吗就像Qt一样——那些我必须知道的东西肯定会留下来未来安全吗?
QSharedPointer
)是在堆栈上创建的,在它们死后没有其他对象可以访问它们。然而,崩溃发生在他们死亡后的一段随机时期。以前有人遇到过这样的问题吗?请分享你的经验。希望这些问题和我的帖子能帮助其他人避免我在过去24小时里所经历的地狱。
这个问题已经在Qt 5中解决了,参见https://codereview.qt-project.org/#change,26974
编译器调用错误的析构函数或假设不同的内存布局可能导致某种类型的内存损坏。我想说编译器应该给出一个错误,而不是一个警告。
您将在std::unique_ptr
中遇到类似的问题,如果与不完整类型一起使用,也可能导致析构函数失效。当然,修复非常简单——我为类声明了一个构造函数,然后在实现文件中将其定义为
MyClass::~MyClass() = default;
对于std::unique_ptr
而不是std::shared_ptr
来说,这是一个问题,原因是析构函数是前者类型的一部分,但却是后者的成员。
- C++核心准则 C35 对于接口类"A base class destructor should be either public and virtual, or protected and nonv
- 合法的方式将destructor信息存储到void*
- 检查destructor是否正在破坏所有动态内存
- 无法删除在destructor中的成员指针的课程
- 简单链接列表程序中的Destructor segfault
- IS类型INFO自动更新了以前调用Destructor
- 如何解决"expected constructor, destructor, or type conversion before ‘(’ token"错误?
- {app.exe!_com_error::'vector delete destructor'(unsigned int)}
- 我应该重置Destructor中的原始成员变量
- 从Q开始关闭QDialog时,请致电Destructor
- 为什么构造函数的数量与Destructor调用的数量不匹配
- 用const_iterator成员调用类destructor时堆积损坏
- 当我要退出应用程序时,如何在QT中自动调用destructor
- 在MSVC上的数组初始化期间,destructor在不复制或移动构造方的情况下调用
- 为什么不在C++中调用Destructor
- "Destructor already defined"专用析构函数
- 规则"A user-defined but do-nothing destructor is also a non-trivial destructor"太严格了?
- 明确调用`int` destructor-为什么需要类型的别名
- 构造函数 destructor或对OOP的理解
- Pimpl + QSharedPointer语言 - Destructor = Disaster