Qt:信号和槽错误:对于`vtable的未定义引用

20

以下示例取自此链接:http://developer.kde.org/documentation/books/kde-2.0-development/ch03lev1sec3.html

#include <QObject>
#include <QPushButton>
#include <iostream>
using namespace std;

class MyWindow : public QWidget
{
    Q_OBJECT  // Enable slots and signals
    public:
        MyWindow();
    private slots:
        void slotButton1();
        void slotButton2();
        void slotButtons();
    private:
        QPushButton *button1;
        QPushButton *button2;
};

MyWindow :: MyWindow() : QWidget()
{
    // Create button1 and connect button1->clicked() to this->slotButton1()
    button1 = new QPushButton("Button1", this);
    button1->setGeometry(10,10,100,40);
    button1->show();
    connect(button1, SIGNAL(clicked()), this, SLOT(slotButton1()));

    // Create button2 and connect button2->clicked() to this->slotButton2()
    button2 = new QPushButton("Button2", this);
    button2->setGeometry(110,10,100,40);
    button2->show();
    connect(button2, SIGNAL(clicked()), this, SLOT(slotButton2()));

    // When any button is clicked, call this->slotButtons()
    connect(button1, SIGNAL(clicked()), this, SLOT(slotButtons()));
    connect(button2, SIGNAL(clicked()), this, SLOT(slotButtons()));
}

// This slot is called when button1 is clicked.
void MyWindow::slotButton1()
{
    cout << "Button1 was clicked" << endl;
}

// This slot is called when button2 is clicked
void MyWindow::slotButton2()
{
    cout << "Button2 was clicked" << endl;
}

// This slot is called when any of the buttons were clicked
void MyWindow::slotButtons()
{
    cout << "A button was clicked" << endl;
}

int main ()
{
    MyWindow a;
}

得到的结果是:

    [13:14:34 Mon May 02] ~/junkPrograms/src/nonsense  $make
g++ -c -m64 -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED -I/opt/qtsdk-2010.05/qt/mkspecs/linux-g++-64 -I. -I/opt/qtsdk-2010.05/qt/include/QtCore -I/opt/qtsdk-2010.05/qt/include/QtGui -I/opt/qtsdk-2010.05/qt/include -I. -I. -o signalsSlots.o signalsSlots.cpp
g++ -m64 -Wl,-O1 -Wl,-rpath,/opt/qtsdk-2010.05/qt/lib -o nonsense signalsSlots.o    -L/opt/qtsdk-2010.05/qt/lib -lQtGui -L/opt/qtsdk-2010.05/qt/lib -L/usr/X11R6/lib64 -lQtCore -lpthread
signalsSlots.o: In function `MyWindow::MyWindow()':
signalsSlots.cpp:(.text+0x1a2): undefined reference to `vtable for MyWindow'
signalsSlots.cpp:(.text+0x1aa): undefined reference to `vtable for MyWindow'
signalsSlots.o: In function `MyWindow::MyWindow()':
signalsSlots.cpp:(.text+0x3e2): undefined reference to `vtable for MyWindow'
signalsSlots.cpp:(.text+0x3ea): undefined reference to `vtable for MyWindow'
signalsSlots.o: In function `main':
signalsSlots.cpp:(.text+0x614): undefined reference to `vtable for MyWindow'
signalsSlots.o:signalsSlots.cpp:(.text+0x61d): more undefined references to `vtable for MyWindow' follow
collect2: ld returned 1 exit status
make: *** [nonsense] Error 1

vtable是用于虚函数的。据我所知,这里出错的原因是什么?


我没看出有什么问题,但我想指出你正在使用的教程相当过时了。我建议你查看Qt 4.7文档中的教程。 - ypnos
@ypnos 谢谢,但是4.7版本的教程没有展示任何可以“运行”的例子,我无法直接复制粘贴并运行 :( - Aquarius_Girl
5个回答

36

看起来 moc 没有为您的 QObject 生成代码,因为您在 .cpp 文件中声明它。最简单的解决方法是将 MyWindow 的声明移动到头文件中,并将头文件添加到 HEADERS 列表中,在 .pro 文件中:

HEADERS += yourheader.h 

然后重新运行qmake

(请注意,您查看的KDE 2.0书籍已经过时。)


谢谢您的回复,但是难道没有办法让声明和定义都在.cpp文件中吗? - Aquarius_Girl
尝试使用 HEADERS += your.cpp 。通常,虽然这是可能的,但我发现 QObject 声明非常痛苦,因此我更喜欢将它们作为(私有)头文件来处理。这在所有构建系统中都可以很好地工作,无论是 qmake、cmake 还是其他。 - Frank Osterfeld
再次感谢,我已经将.h、.cpp和主函数:mad:分离,这解决了错误问题。我还没有详细了解moc文件。 - Aquarius_Girl
17
每当你在filename.cpp文件中声明一个QObject时,应该在.cpp文件末尾添加#include "filename.moc",qmake将为你处理正确的事情。例如,在KDE中这是常见做法。 - andref
非常好的答案。Qt在C++本身之上强加了额外的限制,这有点让人烦恼,但我想我可以接受。 - paxdiablo

23

或许有些晚了,但我有同样的问题并花费了一段时间来查找它的来源。

右键单击您的项目,选择“运行qmake”以进行MOC类的新构建。通常它会自动运行...

moc编译器在moc_xxxx.cpp中生成存根和调用,并生成vtable相关内容。


我不使用qtcreator。在向这里提问之前,我确实多次运行了qmake。但是没有帮助。 - Aquarius_Girl
同感。对我来说运行qmake并不能解决任何问题。如果我运行qmake --project,那么它会重新生成.pro文件,然后我就可以编译了,但这会破坏其他人对.pro文件所做的所有更改,这真的很糟糕。 - Freedom_Ben
2
1++ 这解决了我的问题。 - Ivan
问题解决了!谢谢,点个赞! - normalUser
1
在 .h 文件中添加 Q_OBJECT 并运行 qmake 后,它正常工作 +1。 - ffttyy

1

只需将您的Main()更改如下:

#include <QtGui/QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MyWindow w;

    w.show();

    return a.exec();
}

1

根据上面andref的评论,当所有内容都在一个cpp文件中,比如stuff.cpp,你需要在文件末尾添加:

#include "moc_stuff.cpp"

(这是使用Qt 4.7.0)


使用Qt 4.8,这对我没有起作用。我不得不按照所提到的评论建议去做。 - tshepang

0

这个问题可能是由以下一些原因引起的。我有时也会偶尔遇到它们。


你的类头可能缺少 Q_OBJECT 宏。像其他答案已经指出的那样,在头文件中添加宏。

class MyWidg : public QWidget
{
    Q_OBJECT

你的类头文件可能无法被 moc 解析。请在你的 .pro(或 .pri)项目文件的 HEADERS 定义中添加头文件。

HEADERS += file.h

如果上述都不是真的,那么你可能需要再次运行qmake以确保moc重新解析了头文件,以防Q_OBJECT宏在qmake运行后添加到头文件中。只需再次运行qmake即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接