复制到 QMap 时出现 QString 错误。范围问题?
每当我尝试将 QString 添加到 QMap 作为 QWidget 类的析构函数中的键时,我目前都会遇到段错误(有时由于错误的 malloc 导致的 sigabrts) - 我认为这与 QString 的隐式共享模型有关范围。
我有一个 QWidget 充当 MDI 中的子窗口,该 QWidget 有一些 QGLWidget 派生视口实例作为子项。在子窗口中有一个QMap< QString、QVariant >包含项目文件设置的包装类,当子窗口关闭时,它的析构函数调用 QWidget::deleteChildren() 来删除每个视口。在视口析构函数中,当前设置保存到子窗口的设置中,例如:
QString dir = QString( "camera/" ) + name;
sWin.setSetting( dir + "/Projection",
static_cast< int >( camera_->getProjectionType() ) );
为我希望保存的每个属性调用 sWin.setSetting(),'sWin' 是对 QMap 包装类的引用,该类在子窗口析构函数结束。一切都很好,直到 setSetting() 调用,这只是:
inline void setSetting( const QString& key, QVariant value )
{
// projectSettings_ is a standard QMap< QString, QVariant >.
projectSettings_.insert( key, value );
}
此设置对于第一个视口工作正常,在第二个视口的第一次 setSetting() 调用时,出现段错误:
inline QString::QString(const QString &other) : d(other.d)
{ Q_ASSERT(&other != this); d->ref.ref(); }
我是否看到当我的 QString 引用传递给 QMap 时,深层复制失败?如果是这样,为什么?我创建的 QString 尚未超出范围,因为析构函数尚未返回。为什么这会在第二个视口而不是第一个视口上失败?
有时,我会在第一个代码示例的 setSetting() 行中得到一个 sigabrt malloc():内存损坏 in operator+ 。但同样,在第二个视口被破坏开始时,而不是第一个视口被破坏时。
对于这个非常冗长的问题,我深表歉意,但是涉及到的大量代码分布在许多翻译单元中。这个问题的任何线索都会有很大的帮助!
提前致谢。 Cam
更新
我将第一个代码示例更改为:
QString* dir = new QString( "camera/" );
*dir += name;
sWin.setSetting( *dir + "/Projection",
static_cast< int >( camera_->getProjectionType() ) );
作为范围问题的测试。它有时有效,而其他时候则给出完全相同的错误;所以推测堆 QString 正在被破坏,这仅取决于它的内存位置是否已被覆盖。但这也没有意义,因为我没有对其调用删除,也没有关闭应用程序(只是一个 MDI 子窗口)!
更多代码
当实例化新的子窗口时,sWin 会在堆上创建。它被带入视口类的析构函数中,作为我的子窗口中的方法的引用:
inline Sy_project& getProject() { return *project_; }
Sy_project 是(当前)QMap 的简单包装类。我完整的视口基类析构函数是这样的(它在这里失败,而不是在派生类中):
Sy_abstractGLViewport::~Sy_abstractGLViewport()
{
// Write last viewport settings.
QString name = objectName();
Sy_project& sWin = projWindow_->getProject();
QString dir = QString( "camera/" ) + name;
// Avoid "taking address of temporary" warning.
QVector3D pos = camera_->getPosition();
QVector3D foc = camera_->getFocalPoint();
// Save camera settings. Dies on first setSetting() call.
sWin.setSetting( dir + "/Projection",
static_cast< int >( camera_->getProjectionType() ) );
sWin.setSetting( dir + "/position",
QVariant( 84, static_cast< void* >( &pos ) ) );
sWin.setSetting( dir + "/focalPoint",
QVariant( 84, static_cast< void* >( &foc ) ) );
sWin.setSetting( dir + "/FOV", camera_->getFOV() );
sWin.setSetting( dir + "/nearClip", camera_->getPerspectiveClipRange()[0] );
sWin.setSetting( dir + "/farClip", camera_->getPerspectiveClipRange()[1] );
delete camera_;
}
Valgrind
在使用 Valgrind 的 memcheck 之后,我发现了许多看起来与我的堆栈跟踪类似的条目。以前从未使用过它,我仍在破译,但这是否是说我的 Sy_project 类(QMap 的包装器)在 setSetting() 调用后已被删除,从而使 QMap 具有无效引用?
==12418== Invalid read of size 4
==12418== at 0x805D872: QMap<QString, QVariant>::detach_helper() (qmap.h:730)
==12418== by 0x805D380: QMap<QString, QVariant>::detach() (in /home/cbamber85/workspace/Syren GUI/Syren)
==12418== by 0x805CDEE: QMap<QString, QVariant>::insert(QString const&, QVariant const&) (qmap.h:537)
==12418== by 0x805CA33: Sy_project::setSetting(QString const&, QVariant) (Sy_project.h:50)
==12418== by 0x805A78C: Sy_abstractGLViewport::~Sy_abstractGLViewport() (Sy_abstractGLViewport.cpp:67)
==12418== by 0x808EDBC: Sy_QtGLViewport::~Sy_QtGLViewport() (Sy_QtGLViewport.cpp:91)
==12418== by 0x808EE0E: Sy_QtGLViewport::~Sy_QtGLViewport() (Sy_QtGLViewport.cpp:100)
==12418== by 0x4D66D63: QObjectPrivate::deleteChildren() (in /usr/lib/libQtCore.so.4.6.3)
==12418== by 0x4306DDF: QWidget::~QWidget() (in /usr/lib/libQtGui.so.4.6.3)
==12418== by 0x46FBE0E: QFrame::~QFrame() (in /usr/lib/libQtGui.so.4.6.3)
==12418== by 0x475F173: QSplitter::~QSplitter() (in /usr/lib/libQtGui.so.4.6.3)
==12418== by 0x475F1D1: QSplitter::~QSplitter() (in /usr/lib/libQtGui.so.4.6.3)
==12418== Address 0xacf5b9c is 4 bytes inside a block of size 8 free'd
==12418== at 0x40266AD: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==12418== by 0x808D60F: Sy_project::~Sy_project() (Sy_project.h:30)
==12418== by 0x808C9C6: Sy_subWindow::~Sy_subWindow() (Sy_subWindow.cpp:55)
==12418== by 0x808CA84: Sy_subWindow::~Sy_subWindow() (Sy_subWindow.cpp:57)
==12418== by 0x4D66482: qDeleteInEventHandler(QObject*) (in /usr/lib/libQtCore.so.4.6.3)
==12418== by 0x4D67967: QObject::event(QEvent*) (in /usr/lib/libQtCore.so.4.6.3)
==12418== by 0x4302ACB: QWidget::event(QEvent*) (in /usr/lib/libQtGui.so.4.6.3)
==12418== by 0x42A9C63: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /usr/lib/libQtGui.so.4.6.3)
==12418== by 0x42B1CA3: QApplication::notify(QObject*, QEvent*) (in /usr/lib/libQtGui.so.4.6.3)
==12418== by 0x806010F: Sy_application::notify(QObject*, QEvent*) (Sy_application.cpp:14)
==12418== by 0x4D54E0D: QCoreApplication::notifyInternal(QObject*, QEvent*) (in /usr/lib/libQtCore.so.4.6.3)
==12418== by 0x4D589B3: QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (in /usr/lib/libQtCore.so.4.6.3)
I'm currently stuck with regards to segfaults (sometimes sigabrts due to bad mallocs) whenever I try add a QString to a QMap as key inside the destructor of QWidget class I have - I think it has something to do with QString's implicit sharing model and scope.
I have a QWidget acting as a sub window in an MDI, this QWidget has a few QGLWidget derived viewport instances as children. In the sub window there is a QMap< QString, QVariant > wrapper class that contains the project file's settings, when the sub window is closed, it's destructor calls QWidget::deleteChildren() which deletes each of the viewports. In the viewport destructor the current settings are saved into the sub window's settings, eg:
QString dir = QString( "camera/" ) + name;
sWin.setSetting( dir + "/Projection",
static_cast< int >( camera_->getProjectionType() ) );
The sWin.setSetting() is called for each of the properties I wish to save, 'sWin' is a reference to the QMap wrapper class that is deleted at the end of the sub window destructor. Everything is fine until the setSetting() call, which is just:
inline void setSetting( const QString& key, QVariant value )
{
// projectSettings_ is a standard QMap< QString, QVariant >.
projectSettings_.insert( key, value );
}
This setup works fine for the first viewport, at the first setSetting() call of the second, a segfault occurs at:
inline QString::QString(const QString &other) : d(other.d)
{ Q_ASSERT(&other != this); d->ref.ref(); }
Am I seeing a deep copy fail when my QString reference is passed to the QMap? If so, why? The QString I created has not gone out of scope yet as the destructor has not returned. And why would this fail on the second viewport and not the first?
Occasionally I will get a sigabrt malloc(): memory corruption in operator+ in the setSetting() line of the first code example . But again, at the start of the second viewport's destruction, not the first.
I apologise for the very wordy question, but there is a large amount of code involved spread across many translation units. Any clues for this problem would be a great help!
Thanks in advance.
Cam
Update
I changed my first code sample to:
QString* dir = new QString( "camera/" );
*dir += name;
sWin.setSetting( *dir + "/Projection",
static_cast< int >( camera_->getProjectionType() ) );
As a test for scope issues. And it sometimes works, and other times gives exactly the same error; so presumably the heap QString is being destroyed, it just depends on whether or not it's memory location has been overwritten. But this also makes no sense, as I am not calling delete on it, nor am I closing the application (just an MDI sub window)!
More Code
sWin is created on the heap when a new sub window is instantiated. It is brought into the viewport class' destructor as a reference from a method from my sub window:
inline Sy_project& getProject() { return *project_; }
Sy_project is (currently) a simple wrapper class for QMap. My complete viewport base class destructor is this (it fails here, not in the derived class):
Sy_abstractGLViewport::~Sy_abstractGLViewport()
{
// Write last viewport settings.
QString name = objectName();
Sy_project& sWin = projWindow_->getProject();
QString dir = QString( "camera/" ) + name;
// Avoid "taking address of temporary" warning.
QVector3D pos = camera_->getPosition();
QVector3D foc = camera_->getFocalPoint();
// Save camera settings. Dies on first setSetting() call.
sWin.setSetting( dir + "/Projection",
static_cast< int >( camera_->getProjectionType() ) );
sWin.setSetting( dir + "/position",
QVariant( 84, static_cast< void* >( &pos ) ) );
sWin.setSetting( dir + "/focalPoint",
QVariant( 84, static_cast< void* >( &foc ) ) );
sWin.setSetting( dir + "/FOV", camera_->getFOV() );
sWin.setSetting( dir + "/nearClip", camera_->getPerspectiveClipRange()[0] );
sWin.setSetting( dir + "/farClip", camera_->getPerspectiveClipRange()[1] );
delete camera_;
}
Valgrind
After using Valgrind's memcheck I discovered numerous entries that looked similar to my stacktrace. Having never used it before, I'm still deciphering, but is this saying that my Sy_project class (wrapper for QMap) has been deleted after the setSetting() call leaving QMap with an invalid reference?
==12418== Invalid read of size 4
==12418== at 0x805D872: QMap<QString, QVariant>::detach_helper() (qmap.h:730)
==12418== by 0x805D380: QMap<QString, QVariant>::detach() (in /home/cbamber85/workspace/Syren GUI/Syren)
==12418== by 0x805CDEE: QMap<QString, QVariant>::insert(QString const&, QVariant const&) (qmap.h:537)
==12418== by 0x805CA33: Sy_project::setSetting(QString const&, QVariant) (Sy_project.h:50)
==12418== by 0x805A78C: Sy_abstractGLViewport::~Sy_abstractGLViewport() (Sy_abstractGLViewport.cpp:67)
==12418== by 0x808EDBC: Sy_QtGLViewport::~Sy_QtGLViewport() (Sy_QtGLViewport.cpp:91)
==12418== by 0x808EE0E: Sy_QtGLViewport::~Sy_QtGLViewport() (Sy_QtGLViewport.cpp:100)
==12418== by 0x4D66D63: QObjectPrivate::deleteChildren() (in /usr/lib/libQtCore.so.4.6.3)
==12418== by 0x4306DDF: QWidget::~QWidget() (in /usr/lib/libQtGui.so.4.6.3)
==12418== by 0x46FBE0E: QFrame::~QFrame() (in /usr/lib/libQtGui.so.4.6.3)
==12418== by 0x475F173: QSplitter::~QSplitter() (in /usr/lib/libQtGui.so.4.6.3)
==12418== by 0x475F1D1: QSplitter::~QSplitter() (in /usr/lib/libQtGui.so.4.6.3)
==12418== Address 0xacf5b9c is 4 bytes inside a block of size 8 free'd
==12418== at 0x40266AD: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==12418== by 0x808D60F: Sy_project::~Sy_project() (Sy_project.h:30)
==12418== by 0x808C9C6: Sy_subWindow::~Sy_subWindow() (Sy_subWindow.cpp:55)
==12418== by 0x808CA84: Sy_subWindow::~Sy_subWindow() (Sy_subWindow.cpp:57)
==12418== by 0x4D66482: qDeleteInEventHandler(QObject*) (in /usr/lib/libQtCore.so.4.6.3)
==12418== by 0x4D67967: QObject::event(QEvent*) (in /usr/lib/libQtCore.so.4.6.3)
==12418== by 0x4302ACB: QWidget::event(QEvent*) (in /usr/lib/libQtGui.so.4.6.3)
==12418== by 0x42A9C63: QApplicationPrivate::notify_helper(QObject*, QEvent*) (in /usr/lib/libQtGui.so.4.6.3)
==12418== by 0x42B1CA3: QApplication::notify(QObject*, QEvent*) (in /usr/lib/libQtGui.so.4.6.3)
==12418== by 0x806010F: Sy_application::notify(QObject*, QEvent*) (Sy_application.cpp:14)
==12418== by 0x4D54E0D: QCoreApplication::notifyInternal(QObject*, QEvent*) (in /usr/lib/libQtCore.so.4.6.3)
==12418== by 0x4D589B3: QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (in /usr/lib/libQtCore.so.4.6.3)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为您的指针和引用所指向的 Sy_project 对象在调用 Sy_abstractGLViewport 的析构函数时已经被销毁。如果您查看 valgrind 列表,会发现 Sy_project 的析构函数在 Sy_abstractGLViewport 的析构函数之前调用。
因此,当您调用内联 Sy_project& getProject() { 返回 *project_; } 在 Sy_abstractGLViewport 析构函数中,您正在取消引用悬空指针。
I think the Sy_project object your pointer and therefore references are pointing to is already destroyed by the time the destructor of Sy_abstractGLViewport is called. If you look into the valgrind listing, the destructor of Sy_project is called before the destructor of Sy_abstractGLViewport.
So when you call
inline Sy_project& getProject() { return *project_; }
within the Sy_abstractGLViewport destructor, you're dereferencing a dangling pointer.嗯...如果您将代码更改为以下内容,会发生什么:
按照您执行临时副本的方式,创建一个临时副本,然后绑定到对 const 的引用,这应该没问题。之后,QMap 将尝试从该引用复制参数,在本例中,将通过 QString 的隐式共享机制。我只是想知道里面会出什么问题...
Hum... What happens if you change your code for something like this:
In the way you are doing a temporary copy is create and then bound to a reference-to-const, which should be ok. Afterwards, from this reference QMap will attempt to copy the argument, in this case, going through the implicit sharing mechanism of QString. I'm just wondering what could go wrong in there...