Qt-Buch: undef. ref. to vtable for...



  • Hi,

    ich arbeite mich gerade durch das Buch "C++ GUI Programmierung mit Qt4" und stoße dabei in Kap. 2 beim Erstellen des GoToCellDialogs auf ein Problem.

    Ich habe dabei mit dem Qt-Designer eine *.ui Datei erstellt und
    qmake -project
    und
    qmake gotocell.pro
    audgeführt.

    Das ganze hab ich dann (über Code::Blocks) mit MinGW kompiliert.

    (Über CodeBlocks deshalb, weil Qt nicht in meinen Systempfaden ist, ich also Qt-Projekte nicht über die Konsole kompilieren kann. Das sollte aber eigentlich keinen Unterschied machen. Ob die Konsole oder CB MinGW aufruft, ist dem Compiler ja egal...)

    Soweit klappt das prima, das Programm wird korrekt kompiliert, gelinkt und ausgeführt.

    Dann geht das Buch einen Schritt weiter:
    Mittels qmake wurde aus der *.ui-Datei ein Header names ui_gotocelldialog.h erstellt, welcher die Klasse Ui::GoToCellDialog enthält.

    Um dem ganzen jetzt noch Funktionen hinzuzufügen, soll ich eine gotocelldialog.h und eine gotocelldialog.cpp erstellen, welche die Klasse GoToCellDialog enthält.
    Diese wird von Ui::GoToCellDialog und QDialog abgeleitet und bindet sonst nur einige neue Funktionen ein.

    Kompilieren lässt sich das ganze, aber der Linker stürzt ab mit:
    "File Line Message
    (...)\gotocelldialog.cpp 8 'undefined reference to vtable for GoToCellDialog'
    Die Nachricht dann viermal.

    Jetzt noch die passenden Dateien:
    main.cpp

    #include <QApplication>
    
    #include "gotocelldialog.h"
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        GoToCellDialog *dialog = new GoToCellDialog;
        dialog->show();
        return app.exec();
    }
    

    gotocelldialog.h

    #ifndef GOTOCELLDIALOG_H
    #define GOTOCELLDIALOG_H
    
    #include <QDialog>
    
    #include "ui_gotocelldialog.h"
    
    class GoToCellDialog : public QDialog, public Ui::GoToCellDialog
    {
        Q_OBJECT
    
    public:
        GoToCellDialog(QWidget *parent = 0);
    
    private slots:
        void on_lineEdit_textChanged();
    };
    
    #endif
    

    gotocelldialog.cpp

    #include <QtGui>
    
    #include "gotocelldialog.h"
    
    GoToCellDialog::GoToCellDialog(QWidget *parent)
        : QDialog(parent)
    {
        setupUi(this);
    
        QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}");
        lineEdit->setValidator(new QRegExpValidator(regExp, this));
    
        connect(okButton, SIGNAL(clicked()), this, SLOT(accept()));
        connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
    }
    
    void GoToCellDialog::on_lineEdit_textChanged()
    {
        okButton->setEnabled(lineEdit->hasAcceptableInput());
    }
    

    Danke schonmal für jeden Hilfe,

    Sinthoras

    Edit:
    Hier auch noch die automatisch erstellte Header-Datei ui_gotocelldialog.h:

    /********************************************************************************
    ** Form generated from reading ui file 'gotocelldialog.ui'
    **
    ** Created: Sat 5. May 16:58:24 2007
    **      by: Qt User Interface Compiler version 4.2.3
    **
    ** WARNING! All changes made in this file will be lost when recompiling ui file!
    ********************************************************************************/
    
    #ifndef UI_GOTOCELLDIALOG_H
    #define UI_GOTOCELLDIALOG_H
    
    #include <QtCore/QVariant>
    #include <QtGui/QAction>
    #include <QtGui/QApplication>
    #include <QtGui/QButtonGroup>
    #include <QtGui/QHBoxLayout>
    #include <QtGui/QLabel>
    #include <QtGui/QLineEdit>
    #include <QtGui/QPushButton>
    #include <QtGui/QSpacerItem>
    #include <QtGui/QVBoxLayout>
    #include <QtGui/QWidget>
    
    class Ui_GoToCellDialog
    {
    public:
        QVBoxLayout *vboxLayout;
        QHBoxLayout *hboxLayout;
        QLabel *label;
        QLineEdit *lineEdit;
        QHBoxLayout *hboxLayout1;
        QSpacerItem *spacerItem;
        QPushButton *okButton;
        QPushButton *cancelButton;
    
        void setupUi(QWidget *GoToCellDialog)
        {
        GoToCellDialog->setObjectName(QString::fromUtf8("GoToCellDialog"));
        vboxLayout = new QVBoxLayout(GoToCellDialog);
        vboxLayout->setSpacing(6);
        vboxLayout->setMargin(9);
        vboxLayout->setObjectName(QString::fromUtf8("vboxLayout"));
        hboxLayout = new QHBoxLayout();
        hboxLayout->setSpacing(6);
        hboxLayout->setMargin(0);
        hboxLayout->setObjectName(QString::fromUtf8("hboxLayout"));
        label = new QLabel(GoToCellDialog);
        label->setObjectName(QString::fromUtf8("label"));
    
        hboxLayout->addWidget(label);
    
        lineEdit = new QLineEdit(GoToCellDialog);
        lineEdit->setObjectName(QString::fromUtf8("lineEdit"));
    
        hboxLayout->addWidget(lineEdit);
    
        vboxLayout->addLayout(hboxLayout);
    
        hboxLayout1 = new QHBoxLayout();
        hboxLayout1->setSpacing(6);
        hboxLayout1->setMargin(0);
        hboxLayout1->setObjectName(QString::fromUtf8("hboxLayout1"));
        spacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
    
        hboxLayout1->addItem(spacerItem);
    
        okButton = new QPushButton(GoToCellDialog);
        okButton->setObjectName(QString::fromUtf8("okButton"));
        okButton->setEnabled(false);
        okButton->setDefault(true);
    
        hboxLayout1->addWidget(okButton);
    
        cancelButton = new QPushButton(GoToCellDialog);
        cancelButton->setObjectName(QString::fromUtf8("cancelButton"));
    
        hboxLayout1->addWidget(cancelButton);
    
        vboxLayout->addLayout(hboxLayout1);
    
        label->setBuddy(lineEdit);
        QWidget::setTabOrder(lineEdit, okButton);
        QWidget::setTabOrder(okButton, cancelButton);
    
        retranslateUi(GoToCellDialog);
    
        QSize size(216, 73);
        size = size.expandedTo(GoToCellDialog->minimumSizeHint());
        GoToCellDialog->resize(size);
    
        QMetaObject::connectSlotsByName(GoToCellDialog);
        } // setupUi
    
        void retranslateUi(QWidget *GoToCellDialog)
        {
        GoToCellDialog->setWindowTitle(QApplication::translate("GoToCellDialog", "Go to Cell", 0, QApplication::UnicodeUTF8));
        label->setText(QApplication::translate("GoToCellDialog", "&CellLocation:", 0, QApplication::UnicodeUTF8));
        okButton->setText(QApplication::translate("GoToCellDialog", "OK", 0, QApplication::UnicodeUTF8));
        cancelButton->setText(QApplication::translate("GoToCellDialog", "Cancel", 0, QApplication::UnicodeUTF8));
        Q_UNUSED(GoToCellDialog);
        } // retranslateUi
    
    };
    
    namespace Ui {
        class GoToCellDialog: public Ui_GoToCellDialog {};
    } // namespace Ui
    
    #endif // UI_GOTOCELLDIALOG_H
    

  • Mod

    Kenn mich nicht so direkt mit QT aus.
    Aber die Fehlermeldung deutet darauf hin, das bei der Vererbung evtl. nicht ganz rund läuft.
    Evtl. ist eine Methode nicht überladen, oder ein Define fehlt.



  • (...) oder ein Define fehlt.

    Meinst du damit eine #define -Präprozessordirektive oder was anderes
    (kann mir irgendwie nicht vorstellen, wo das hier an #define liegen sollte)?

    (...)Evtl. ist eine Methode nicht überladen(...)

    Es müssen doch nur virtuelle Methoden überladen werden, oder sehe ich das falsch?
    (In ui_gotocell.h gibt es keine)



  • So, ich hab jetzt, um zu testen, ob etwas bei der Vererbung falsch läuft, alles manuell in eine Klasse gepackt.
    Kompiliert wird es, nur der Linker meckert wieder.
    Dieses mal aber auf etwas seltsame Art und Weise:

    File...................................................................................Line...Message
    C:\Qt\4.2.3\include\QtCore\..\..\src\corelib\global\qglobal.h:: undefined reference to vtable for Ui_GoToCellDialog' )]+0x7e):C:\\Qt\\4.2.3\\include\\QtCore\\..\\..\\src\\corelib\\global\\qglobal.h:: undefined reference tovtable for Ui_GoToCellDialog'

    Stimmt da was mit Qt nicht?



  • Ich hatte das problem auch mal. Die lösung war es alles in der konsole zu machen.
    Ich würde die systempfade einfach ändern und qt hinzufügen.
    Bei mir gings (nachdem ich im makefile ein paar \ durch / ersetzt habe)
    Es geht beim Compiler aufruf auch darum das die libs mitgegeben sind.



  • Sinthoras schrieb:

    C:\Qt\4.2.3\include\QtCore\..\..\src\corelib\global\qglobal.h:: undefined reference to vtable for Ui_GoToCellDialog' )]+0x7e):C:\\Qt\\4.2.3\\include\\QtCore\\..\\..\\src\\corelib\\global\\qglobal.h:: undefined reference tovtable for Ui_GoToCellDialog'

    Des Problem hab ich auch grad!!



  • Fügt einen Destruktor hinzu (auch wenn der leer ist) und implementiert den in einer Code-Datei.

    Das Problem tritt auf, weil der GCC den vtable immer in dem Objekt anlegt, in dem der Destruktor implementiert wird. Gibt es keinen Destruktor und befindet sich der Code in einem Header weiß der GCC also nicht wo er den vtable anlegen bzw. suchen soll.



  • Also bei mir kompiliert das Beispiel ohne Probleme:

    kde@barney:~/glhp> /home/kde/software/qt43/bin/qmake -project
    kde@barney:~/glhp> /home/kde/software/qt43/bin/qmake
    kde@barney:~/glhp> make
    g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I../software/qt43/mkspecs/linux-g++ -I. -I../software/qt43/include/QtCore -I../software/qt43/include/QtCore -I../software/qt43/include/QtGui -I../software/qt43/include/QtGui -I../software/qt43/include -I. -I. -I. -I../software/qt43/include/QtCore -I../software/qt43/include/QtCore -I../software/qt43/include/QtGui -I../software/qt43/include/QtGui -I../software/qt43/include -I. -I. -I. -o gotocelldialog.o gotocelldialog.cpp
    g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I../software/qt43/mkspecs/linux-g++ -I. -I../software/qt43/include/QtCore -I../software/qt43/include/QtCore -I../software/qt43/include/QtGui -I../software/qt43/include/QtGui -I../software/qt43/include -I. -I. -I. -I../software/qt43/include/QtCore -I../software/qt43/include/QtCore -I../software/qt43/include/QtGui -I../software/qt43/include/QtGui -I../software/qt43/include -I. -I. -I. -o main.o main.cpp
    /home/kde/software/qt43/bin/moc -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I../software/qt43/mkspecs/linux-g++ -I. -I../software/qt43/include/QtCore -I../software/qt43/include/QtCore -I../software/qt43/include/QtGui -I../software/qt43/include/QtGui -I../software/qt43/include -I. -I. -I. -I../software/qt43/include/QtCore -I../software/qt43/include/QtCore -I../software/qt43/include/QtGui -I../software/qt43/include/QtGui -I../software/qt43/include -I. -I. -I. gotocelldialog.h -o moc_gotocelldialog.cpp
    g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I../software/qt43/mkspecs/linux-g++ -I. -I../software/qt43/include/QtCore -I../software/qt43/include/QtCore -I../software/qt43/include/QtGui -I../software/qt43/include/QtGui -I../software/qt43/include -I. -I. -I. -I../software/qt43/include/QtCore -I../software/qt43/include/QtCore -I../software/qt43/include/QtGui -I../software/qt43/include/QtGui -I../software/qt43/include -I. -I. -I. -o moc_gotocelldialog.o moc_gotocelldialog.cpp
    g++ -Wl,-rpath,/home/kde/software/qt43/lib -o glhp gotocelldialog.o main.o moc_gotocelldialog.o    -L/home/kde/software/qt43/lib -lQtGui -L/home/kde/software/qt43/lib -L/usr/X11R6/lib -lpng -lSM -lICE -pthread -L/opt/gnome/lib -pthread -lXi -lXrender -lXrandr -lXfixes -lXcursor -lXinerama -lfreetype -lfontconfig -lXext -lX11 -lQtCore -lz -lm -pthread -lgthread-2.0 -lglib-2.0 -lrt -ldl -lpthread
    


  • Ok, vielen Dank soweit für die Hilfe.

    @rüdiger: Wo soll ich den Destruktor implementieren? In der abgeleiteten Klasse oder der Basisklasse?

    @andy1066: Wie muss ich den Qt-Pfad angeben. Einfach C:\Qt\4.2.3\ in die Path-Variable einzutragen hat nicht geholfen (oder ich habs irgendwie falsch gemacht).
    Und in welcher Datei soll ich dann was ersetzen?
    Was muss ich denn dann alles in der Konsole ausführen?

    qmake -project
    qmake test.pro
    make ???
    und dann?

    Entschuldigt bitte die dummen Fragen, aber ich kenn mich mit dem ganzen Kram nicht so gut aus...

    Danke jedenfalls für alle schon geleistete und noch zu leistende Hilfe!

    P.S.: Warum kann ich eingeloggt denn keine Antwort schreiben?
    Die Schaltfläche ist einfach weg... 😕
    Aber auch nur in diesem Thread... 😕 😕 😕



  • Ok, ich hab jetzt einfach mal in allen Klassen einen leeren Destruktor hinzugefügt
    (~Klasse() {}; in den Header).
    An dem Problem ändert sich nichts, immernoch derselbe Fehler.

    Kann mir jemand bei den Pfaden und den Kommandos weiterhelfen, damit ich mal probieren kann, über die Konsole zu kompilieren?


  • Mod

    Der Destruktor muss virtual sein.



  • Hab ihn in der Basisklasse virtual gemacht, in der abgeleiteten "normal".
    Ist doch richtig so, oder nicht?
    Das Problem besteht aber irgendwie weiterhin?

    P.S.: Kann mir bitte mal jemand die Pfadvariablen und Befehle nennen?



  • So, jetzt kann ich auch wieder eingeloggt schreiben.

    Also, wie erwähnt, sieht es bei der abgeleiteten Klasse jetzt so aus:

    #ifndef GOTOCELLDIALOG_H
    #define GOTOCELLDIALOG_H
    
    #include <QDialog>
    #include "ui_gotocelldialog.h"
    
    class GoToCellDialog : public QDialog, public Ui::GoToCellDialog
    {
        Q_OBJECT
    
    public:
        GoToCellDialog(QWidget *parent = 0);
    
        ~GoToCellDialog() {};
    
    private slots:
        void on_lineEdit_textChanged();
    };
    #endif
    

    Die abgeleiteten Klassen sehen jetzt so aus (gekürzt, der Rest ist wie im ersten Beitrag):

    class Ui_GoToCellDialog
    {
    public:
    
        Ui_GoToCellDialog() {};
        virtual ~Ui_GoToCellDialog() {};
    //(...)
    };
    
    namespace Ui {
        class GoToCellDialog: public Ui_GoToCellDialog {
        public:
        GoToCellDialog() {};
        virtual ~GoToCellDialog() {};
        };
    } // namespace Ui
    

    Wie man sieht, habe ich zusätzlich noch default-Konstruktoren eingebaut. Von denen habe ich mir zwar nicht viel erhofft, aber was bessere fällt mir nicht ein...



  • Sinthoras@Bug schrieb:

    So, jetzt kann ich auch wieder eingeloggt schreiben.

    Zu früh gefreut... ich dachte, ich könnte wieder schreiben...



  • Oh, sry ich merke gerade, dass ich mich oben vertan hab:

    Natürlich ist das erste die abgeleitete Klasse und das darunter ist die passende Basisklasse (mit ihrer Basisklasse wiederum).



  • Also, es geht jetzt, wenn auch auf etwas seltsame Art und Weise:

    Zunächst mit der cmd.exe ins Verzeichniss und
    qmake -project
    ausführen.

    Dann die Qt Command Prompt öffnen, in das Verzeichnis wechseln und
    qmake test.pro
    make

    Fertig.
    (Das qmake -project muss ich aber in der Windows-Eingabeauforderung ausführen, weil die Qt Command Prompt das aus irgendeinem Grund nicht peilt.)

    Naja, immerhin geht's jetzt.



  • Ich schätze, es liegt daran, dass Code::Blocks aus irgendeinem Grund moc nicht ausgeführt hat uns deshalb die Makros nicht aufgelöst wurden.

    Wenn jemand eine Möglichkeit kennt, wie ich CB dazu bringen kann, dass es moc mit ausführt, wäre ich ihm natürlich sehr dankbar.

    Vielen Dank,
    Sinthoras


  • Mod

    Such doch mal im offiziellen CB Forum, QT müsste von C::B schon länger unterstützt werden.



  • So, hab was entsprechendes gefunden.
    Ich übernehme einfach mal den Text, nachzulesen hier:

    "I'm not sure if this is the best way, but I have a system I use that runs moc automatically so I don't have to do it manually. There could definitely be a better way, but this works well.

    For each header that declares a class with the Q_OBJECT macro (any class that with signals or slots), I configure the project to compile with a custom build option of "moc header.h -o moc_header.cpp" and add moc_header.cpp to the project.

    Here are the steps to do it using the nightly build of march 16/2007.

    In the project management pane (the one that lists your project files) right click on the header file you want to set up.

    Step 1:

    Choose properties.
    Go to the build tab
    Make sure that "compile file" is checked but "link file" is not checked.
    Go to the advanced tab
    Make sure "choose custom command to build this file" is checked
    In the text box, paste:
    $(#QT)\bin\moc**.exe** header.h -o moc_header.cpp
    (replace header.h and moc_header.cpp with the name of your own headers)
    Click okay

    This assumes you have the global variable qt correctly set and that your version of QT has moc in the bin subdirectory.

    Step 2
    Create a new file
    Choose to add it to the project
    Save it as moc_header.cpp.

    You'll never need to edit this file again, it is generated by moc, you just need to have it in the project and this is the easiest way."

    Das ".exe" habe ich zum Originaltext ergänzt, weil es bei mir ohne Dateiendung nicht lief.



  • Sinthoras@Bug schrieb:

    Ok, vielen Dank soweit für die Hilfe.

    @andy1066: Wie muss ich den Qt-Pfad angeben. Einfach C:\Qt\4.2.3\ in die Path-Variable einzutragen hat nicht geholfen (oder ich habs irgendwie falsch gemacht).

    C:\Qt\4.2.3\bin

    Sinthoras@Bug schrieb:

    Und in welcher Datei soll ich dann was ersetzen?
    Was muss ich denn dann alles in der Konsole ausführen?

    qmake -project
    qmake test.pro
    make ???
    und dann?

    cd ordner (zb c:\qt\test...)
    qmake -project
    qmake
    make
    dann musst du mal im unterordner relaese( oder so) schauen


Anmelden zum Antworten