Pergunta

Atualmente estou preso em relação a segfaults (às vezes sigabrts devido a mallocs ruins) sempre que tento adicionar um QString a um QMap como chave dentro do destruidor da classe QWidget que tenho - acho que tem algo a ver com o modelo de compartilhamento implícito do QString e escopo.

Eu tenho um QWidget agindo como uma subjanela em um MDI, este QWidget tem algumas instâncias de viewport derivadas de QGLWidget como filhos.Na subjanela há uma classe wrapper QMap< QString, QVariant > que contém as configurações do arquivo de projeto, quando a subjanela é fechada, seu destruidor chama QWidget::deleteChildren() que exclui cada uma das viewports.No destruidor da viewport, as configurações atuais são salvas nas configurações da subjanela, por exemplo:

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

O sWin.setSetting() é chamado para cada uma das propriedades que desejo salvar, 'sWin' é uma referência à classe wrapper QMap que é excluída no final do destruidor da subjanela.Tudo está bem até a chamada setSetting(), que é apenas:

inline void setSetting( const QString& key, QVariant value )
{
    //  projectSettings_ is a standard QMap< QString, QVariant >.
    projectSettings_.insert( key, value );
}

Esta configuração funciona bem para a primeira janela de visualização, na primeira chamada setSetting() da segunda, ocorre um segfault em:

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

Estou vendo uma falha na cópia profunda quando minha referência QString é passada para o QMap?Se sim, por quê?O QString que criei ainda não saiu do escopo porque o destruidor não retornou.E por que isso falharia na segunda janela de visualização e não na primeira?

De vez em quando eu pego um sigabrt maloc():corrupção de memória em operador+ na linha setSetting() do primeiro exemplo de código.Mas, novamente, no início da destruição da segunda janela de visualização, não da primeira.

Peço desculpas pela pergunta muito prolixa, mas há uma grande quantidade de código envolvido, espalhado por muitas unidades de tradução.Qualquer pista para este problema seria de grande ajuda!

Desde já, obrigado.Câmera


Atualizar

Mudei meu primeiro exemplo de código para:

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

Como um teste para problemas de escopo.E isso às vezes funciona, e outras vezes dá exatamente o mesmo erro;então, presumivelmente, o heap QString está sendo destruído, depende apenas se o local da memória foi sobrescrito ou não.Mas isso também não faz sentido, pois não estou chamando delete nem fechando o aplicativo (apenas uma subjanela MDI)!


Mais código

sWin é criado no heap quando uma nova subjanela é instanciada.Ele é trazido para o destruidor da classe viewport como uma referência de um método da minha subjanela:

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

Sy_project é (atualmente) uma classe wrapper simples para QMap.Meu destruidor completo da classe base da viewport é este (ele falha aqui, não na classe derivada):

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

Depois de usar o memcheck do Valgrind, descobri inúmeras entradas que pareciam semelhantes ao meu stacktrace.Nunca tendo usado isso antes, ainda estou decifrando, mas isso quer dizer que minha classe Sy_project (wrapper para QMap) foi excluída depois a chamada setSetting() deixando o QMap com uma referência inválida?

==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)
Foi útil?

Solução

Acho que o objeto Sy_project para o qual seu ponteiro e, portanto, as referências estão apontando já está destruído no momento em que o destruidor de Sy_abstractGLViewport é chamado.Se você olhar a listagem do valgrind, o destruidor de Sy_project é chamado antes do destruidor de Sy_abstractGLViewport.

Então, quando você ligar inline Sy_project& getProject() { return *project_; } dentro do destruidor Sy_abstractGLViewport, você está desreferenciando um ponteiro pendente.

Outras dicas

Zumbir...O que acontece se você alterar seu código para algo assim:

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

Da maneira que você está fazendo, uma cópia temporária é criada e depois vinculada a uma referência a const, o que deve estar ok.Posteriormente, a partir desta referência o QMap tentará copiar o argumento, neste caso, passando pelo mecanismo de compartilhamento implícito do QString.Só estou me perguntando o que pode dar errado aí...

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top