Ошибки QString при копировании в QMap.Проблема с областью применения?

StackOverflow https://stackoverflow.com/questions/5048032

Вопрос

В настоящее время я застрял в отношении segfaults (иногда sigabrts из-за плохих mallocs) всякий раз, когда я пытаюсь добавить QString в QMap в качестве ключа внутри деструктора класса QWidget, который у меня есть - я думаю, это как-то связано с моделью неявного совместного использования QString и областью видимости.

У меня есть QWidget, действующий как подокно в MDI, этот QWidget имеет несколько производных от QGLWidget экземпляров viewport в качестве дочерних элементов.Во вложенном окне есть 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() второго segfault происходит при:

inline QString::QString(const QString &other) : d(other.d)
{ Q_ASSERT(&other != this); d->ref.ref(); }

Вижу ли я сбой глубокого копирования, когда моя ссылка на QString передается в QMap?Если да, то почему?Созданная мной QString еще не вышла за пределы области видимости, так как деструктор не вернулся.И почему это приведет к сбою во втором окне просмотра, а не в первом?

Иногда я получаю sigabrt malloc():повреждение памяти в operator+ в строке setSetting() первого примера кода .Но опять же, в начале разрушения второго видового экрана, а не первого.

Я приношу извинения за очень многословный вопрос, но задействовано большое количество кода, разбросанного по многим единицам перевода.Любые подсказки по этой проблеме были бы большим подспорьем!

Заранее благодарю.Кулачок


Обновить

Я изменил свой первый пример кода на:

QString* dir = new QString( "camera/" );
*dir += name;
sWin.setSetting( *dir + "/Projection",
                 static_cast< int >( camera_->getProjectionType() ) );

В качестве теста на наличие проблем с областью применения.И это иногда работает, и в других случаях выдает точно такую же ошибку;таким образом, предположительно, QString кучи уничтожается, это просто зависит от того, была ли перезаписана его ячейка памяти.Но это также не имеет смысла, так как я не вызываю delete для этого и не закрываю приложение (просто подокно MDI)!


Больше кода

sWin создается в куче при создании экземпляра нового подокна.Он переносится в деструктор класса viewport как ссылка на метод из моего подокна:

inline Sy_project& getProject() { return *project_; }

Sy_project - это (в настоящее время) простой класс-оболочка для QMap.Мой полный деструктор базового класса viewport выглядит следующим образом (он завершается неудачей здесь, а не в производном классе):

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

Валгринд

После использования memcheck от Valgrind я обнаружил множество записей, которые выглядели похожими на мой stacktrace.Никогда не используя его раньше, я все еще расшифровываю, но говорит ли это о том, что мой класс 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)
Это было полезно?

Решение

Я думаю, что объект Sy_project, на который указывает ваш указатель и, следовательно, ссылки, уже уничтожен к моменту вызова деструктора Sy_abstractGLViewport.Если вы заглянете в список valgrind, деструктор Sy_project вызывается перед деструктором Sy_abstractGLViewport .

Поэтому, когда ты позвонишь inline Sy_project& getProject() { return *project_; } в деструкторе Sy_abstractGLViewport вы разыменовываете висячий указатель.

Другие советы

Гул...Что произойдет, если вы измените свой код на что-то подобное:

dir.append("/Projection");
sWin.setSetting(dir, static_cast<int>(camera_->getProjectionType()));

В том, как вы это делаете, временная копия создается, а затем привязывается к ссылке на const, которая должна быть в порядке.Впоследствии из этой ссылки QMap попытается скопировать аргумент, в данном случае, используя неявный механизм совместного использования QString.Мне просто интересно, что там может пойти не так...

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top