Speicherzugriffsfehler

Alles zur Entwicklung von KDE - Anwendungen
Antworten
L.Kantack
Beiträge: 2
Registriert: 11. Januar 2010 13:18

Speicherzugriffsfehler

Beitrag von L.Kantack »

Hallo,

beim Beenden der folgenden kleinen Bsp.-Qt-Applikation tritt manchmal ein Speicherzugriffsfehler auf. (Oftmaliges Probieren notwendig)

Lässt man das Programm mit valgrind laufen, erhält man folgende Fehlermeldung:
==7957== Invalid read of size 4
==7957== at 0x4979040: QCursor::~QCursor() (in /usr/lib/libQtGui.so.4.4.3)
==7957== by 0x49AF333: QWidgetPrivate::deleteExtra() (in /usr/lib/libQtGui.so.4.4.3)
==7957== by 0x49AF614: QWidgetPrivate::~QWidgetPrivate() (in /usr/lib/libQtGui.so.4.4.3)
==7957== by 0x4558EE: QObject::~QObject() (in /usr/lib/libQtCore.so.4.4.3)
==7957== by 0x49B452A: QWidget::~QWidget() (in /usr/lib/libQtGui.so.4.4.3)
==7957== by 0x44F400: QObjectPrivate::deleteChildren() (in /usr/lib/libQtCore.so.4.4.3)
==7957== by 0x49B3DC3: QWidget::~QWidget() (in /usr/lib/libQtGui.so.4.4.3)
==7957== by 0x4D001D0: QFrame::~QFrame() (in /usr/lib/libQtGui.so.4.4.3)
==7957== by 0x4D95F3C: QAbstractScrollArea::~QAbstractScrollArea() (in /usr/lib/libQtGui.so.4.4.3)
==7957== by 0x4D79280: QTextEdit::~QTextEdit() (in /usr/lib/libQtGui.so.4.4.3)
==7957== by 0x804B456: ETerminal::~ETerminal() (epicture.cpp:18 )
==7957== by 0x6DF63D: exit (in /lib/libc-2.7.so)
==7957== Address 0x4489F18 is 0 bytes inside a block of size 68 free'd
==7957== at 0x4004E56: operator delete(void*) (vg_replace_malloc.c:244)
==7957== by 0x4978985: (within /usr/lib/libQtGui.so.4.4.3)
==7957== by 0x49C9D4E: (within /usr/lib/libQtGui.so.4.4.3)
==7957== by 0x496BC0E: QApplication::~QApplication() (in /usr/lib/libQtGui.so.4.4.3)
==7957== by 0x804B09A: main (main.cpp:10)
Das gleiche Phänomen tritt auch bei Verwendung eines QLabels auf. Allerdings konnte ich diesen Fehlerfall in einer kleinen Applikation nicht reproduzieren.

Die Bsp.Applikation ist wie folgt aufgebaut:

Code: Alles auswählen

*-*-*-*-*-*-*-*-*-main.cpp*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
#include <QApplication>
#include "ebsp.h"
/
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
   
    ETerminal::getInst().show();

    return a.exec();
}
/*-*-*-*-*-*-*-*-*-ebsp.cpp*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/
#include "ebsp.h"

ETerminal::ETerminal(QWidget *parent):
QWidget(parent),
_EditField(this)
{   
    setGeometry(0,0, 320, 240);

    _EditField.init("x Start");
}

ETerminal::~ETerminal()
{
}

ETerminal& ETerminal::getInst(){
    static ETerminal instance;
    return instance;
}


EEditField::EEditField(QWidget* p):
QTextEdit(p)
{
   setEnabled(true);
   setFocusPolicy(Qt::TabFocus);
   //setFrame(false);
   setFrameShape(QFrame::NoFrame);
   setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
   setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
   setTextInteractionFlags(Qt::TextSelectableByKeyboard);
   setContextMenuPolicy(Qt::NoContextMenu);
   setLineWrapMode(QTextEdit::NoWrap);
   setWordWrapMode(QTextOption::NoWrap);
   setOverwriteMode(true);
   setReadOnly(true);
}

EEditField::~EEditField()
{
}

void EEditField::init (const QString& text){
   setPlainText(text);

   QTextCursor myCursor(textCursor());

   myCursor.setPosition(0, QTextCursor::MoveAnchor);
   //myCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
   myCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); 

   setTextCursor(myCursor);
}


void EEditField::keyPressEvent (QKeyEvent* ev){
   if (ev) {
      unsigned int key = ev->key();

      if ( (Qt::Key)key == Qt::Key_Left) {
         // Zeige Wert A an

         QTextCursor myCursor(textCursor());
         myCursor.removeSelectedText();
      
         QString temp(toPlainText());
         temp.insert(myCursor.position(), "A");
         setPlainText(temp);

         myCursor.setPosition(0, QTextCursor::MoveAnchor);
         //myCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
         myCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); 
         setTextCursor(myCursor);
      }
      else if ( (Qt::Key)key == Qt::Key_Right) {
         // Zeige Wert A an

         QTextCursor myCursor(textCursor());
         myCursor.removeSelectedText();
      
         QString temp(toPlainText());
         temp.insert(myCursor.position(), "B");
         setPlainText(temp);

         myCursor.setPosition(0, QTextCursor::MoveAnchor);
         //myCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
         myCursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); 
         setTextCursor(myCursor);
      }
   }
}
Ich benutze Qt 4.4.3 auf dem Betriebssystem Fedora mit der Desktop-Umgebung KDE.
Vielleicht jage ich ja dort einen Blindgänger, jedoch tritt dieser Speicherfehler bei meiner großen Applikation ziemlich häufig auf, was natürlich nicht so schön ist.

Vielleicht erkennt ja jemand einen bösen Code-Fehler oder sonstiges - ich würde mich über jeden Tipp freuen.

Danke, Grüße :wink:
L.Kantack
phlox81
Beiträge: 97
Registriert: 7. Juli 2009 12:30
Kontaktdaten:

Beitrag von phlox81 »

Diese Art des Singletons könnte ungeeignet für QWidgets sein.
Tritt das auch auf, wenn du das Widget "Normal" auf dem Stack von Main anlegst?
L.Kantack
Beiträge: 2
Registriert: 11. Januar 2010 13:18

Beitrag von L.Kantack »

Vielen Dank für deine schnelle Antwort.

Ich habe mal mein QWidget, wie du gesagt hast dynam. mit new angelegt.

Code: Alles auswählen

ETerminal* ETerminal::getExemplar(){
    if (p) {
       return p;
    }
    else {
       p = new ETerminal();
    }
    return p;
}
Und siehe da, der Fehler ist bisher - nach zahlreichen Versuchen - nicht mehr aufgetreten.
Also schon einmal vielen Dank plox81.

Aber das Wieso erschließt sich mir nicht ganz.:?:
Durch

Code: Alles auswählen

static ETerminal instance
reserviert der Linker Speicherplatz. Dieser sollte eigentlich doch von der Größe korrekt sein.
Wird das Element dann beim Beenden zerstört, werden die Destruktoren irgendwie so aufgerufen, dass ein Destruktor etwas zu löschen versucht, was nicht mehr existiert. Aber - ja - liegt dass dann nicht am Destruktor? Das diesem dann eine Abfrage fehlt, wie if (p)?

Verstehen tue ich das nich so richtig....
Jmd. vllcht eine Erklärung?:wink:
phlox81
Beiträge: 97
Registriert: 7. Juli 2009 12:30
Kontaktdaten:

Beitrag von phlox81 »

Ich tippe mal darauf, das es etwas mit der statischen Instanz zu tun hat.
Diese könnte schon vor main existieren, und somit bevor die Instanz des QApp Objekts existiert. Dieses sollte aber zu erst da sein.
Genauso gut kann es sein, das das Objekt erst nach der Zerstörung von QApp zerstört wird, und somit dort wiederum irgendwo eine Leiche erwischt.

Oder was ganz anderes ;)
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Der zweite von phlox81 genannte Fall ist korrekt.
Der erste kann es nicht sein da es eine lokale statische Variable ist.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Code: Alles auswählen

ETerminal* ETerminal::getExemplar(){
    if (p) {
       return p;
    }
    else {
       p = new ETerminal();
    }
    return p;
}
Das geht auch schöner ;)

Code: Alles auswählen

ETerminal* ETerminal::getExemplar(){
    if (!p) {
       p = new ETerminal();
    }
    return p;
}
Trotzdem würde ich ein QWidget niemals (mehr ;)) als Singleton implementieren.

Singleton impliziert, dass das Widget an mehreren Stellen verwendet werden soll, und zwar immer die selbe Instanz. Widgets liegen für gewähnlich in Layouts. Damit bekommen sie ein neues Parent. Wenn das Layout über den Jordan geht, nimmt es alle children mit. Viel Spaß beim Debuggen, wenn das Widget zerstört wird, aber noch wo anders angezeigt wird ;)
Außerdem musst du daran denken, die Instanz wieder zu zerstören!

Eine bessere Lösung wäre, wenn du das Widget von den anzuzeigenden Daten trennst. Dann kannst du die Datensource als Singleton implementieren. Bei Datenupdates wird ein SIGNAL emittet und alle Widgets können sich unabhängig voneinander aktualisieren.
Da die Datensource für Signals ein QObject sein muss, passt du bitte wieder auf, dass es keinen parent hat, der das Objekt löscht und so wieder nen Segfault o.Ä auslöst ;)
Antworten