error: undefined reference to ‘vtable for …’
这个报警就是子类没有实现父类的纯虚函数,奇怪的是自定义的虚函数都已经进行了定义,这个时候需要留意还有一种情况就是Qt中一个类继承了QObject,而且添加了Q_OBJECT宏,此时就可能出现上面的异常。
原理
测试源码
object.h:
#ifndef OBJECT_H
#define OBJECT_H
#include <QObject>
class Object : public QObject
{
Q_OBJECT // 增加删除这个宏然后再编译
public:
explicit Object(QObject *parent = 0);
};
#endif // OBJECT_H
object.cpp:
#include "object.h"
Object::Object(QObject *parent) :
QObject(parent)
{
}
main.cpp:
#include <QtCore/QCoreApplication>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
return a.exec();
}
Makefile
在Object类中分别添加和删除Q_OBJECT宏,查看对应的两份Makefile:
不包含Q_OBJECT
当一个类中不包含Q_OBJECT,生成的makefile是这样的(精简):
OBJECTS = main.o \
object.o
TARGET = test2
first: all
all: Makefile $(TARGET)
$(TARGET): $(OBJECTS)
$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS)
Makefile: test2.pro
$(QMAKE) -spec /usr/share/qt4/mkspecs/linux-g++ -o Makefile test2.pro
main.o: main.cpp
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o main.o main.cpp
object.o: object.cpp object.h
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o object.o object.cpp
重新编译的操作:
15:35:55: 为项目test2执行构建步骤 …
15:35:55: 正在启动 “/usr/bin/make” clean -w
{1"?} {2?}
make: Entering directory/home/u/delete/test2/test2' rm -f main.o object.o rm -f *~ core *.core make: Leaving directory
/home/u/delete/test2/test2’
15:35:55: 进程"/usr/bin/make"正常退出。
15:35:55: 配置没有改变, 跳过 qmake 步骤.
15:35:55: 正在启动 “/usr/bin/make” -w
{1"?} {2?}
make: Entering directory/home/u/delete/test2/test2' g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_WEBKIT -DQT_NO_DEBUG -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4 -I. -o main.o main.cpp g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_WEBKIT -DQT_NO_DEBUG -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4 -I. -o object.o object.cpp g++ -Wl,-O1 -o test2 main.o object.o -L/usr/lib/i386-linux-gnu -lQtCore -lpthread make: Leaving directory
/home/u/delete/test2/test2’
15:35:55: 进程"/usr/bin/make"正常退出。
包含Q_OBJECT
当一个类中包含Q_OBJECT的时候,对应的makefile是这样的(精简):
OBJECTS = main.o \
object.o \
moc_object.o
TARGET = test2
first: all
all: Makefile $(TARGET)
$(TARGET): $(OBJECTS)
$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJCOMP) $(LIBS)
Makefile: test2.pro
$(QMAKE) -spec /usr/share/qt4/mkspecs/linux-g++ -o Makefile test2.pro
moc_object.cpp: object.h
/usr/bin/moc-qt4 $(DEFINES) $(INCPATH) object.h -o moc_object.cpp
main.o: main.cpp
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o main.o main.cpp
object.o: object.cpp object.h
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o object.o object.cpp
moc_object.o: moc_object.cpp
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o moc_object.o moc_object.cpp
然后有意思的就来了,重新编译的操作如下:
15:34:24: 为项目test2执行构建步骤 …
15:34:24: 正在启动 “/usr/bin/make” clean -w
{1"?} {2?}
make: Entering directory/home/u/delete/test2/test2' rm -f moc_object.cpp rm -f main.o object.o moc_object.o rm -f *~ core *.core make: Leaving directory
/home/u/delete/test2/test2’
15:34:24: 进程"/usr/bin/make"正常退出。
15:34:24: 配置没有改变, 跳过 qmake 步骤.
15:34:24: 正在启动 “/usr/bin/make” -w
{1"?} {2?}
make: Entering directory/home/u/delete/test2/test2' g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_WEBKIT -DQT_NO_DEBUG -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4 -I. -o main.o main.cpp g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_WEBKIT -DQT_NO_DEBUG -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4 -I. -o object.o object.cpp /usr/bin/moc-qt4 -DQT_WEBKIT -DQT_NO_DEBUG -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4 -I. object.h -o moc_object.cpp g++ -c -pipe -O2 -Wall -W -D_REENTRANT -DQT_WEBKIT -DQT_NO_DEBUG -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt4/mkspecs/linux-g++ -I. -I/usr/include/qt4/QtCore -I/usr/include/qt4 -I. -o moc_object.o moc_object.cpp g++ -Wl,-O1 -o test2 main.o object.o moc_object.o -L/usr/lib/i386-linux-gnu -lQtCore -lpthread make: Leaving directory
/home/u/delete/test2/test2’
15:34:25: 进程"/usr/bin/make"正常退出。
两者区别
从两种重新编译的具体执行过程来看,重新编译精简理解就是:
make clean
make
当一开始编译一份不含Q_OBJECT宏的类时,他会生成一份Makefile,这份Makefile是基于没有Q_OBJECT宏qmake创建出来的,以本章例子为例,创建出来的Makefile并没有依赖moc_object.cpp
,也没有创建该源文件的依赖生成关系;而该源文件就是Q_OBJECT宏对应的接口实现,因此,当仅修改头文件、在一个类中添加Q_OBJECT宏,此时即使是重新编译,由于.pro
文件并没有变更,因此Makefile也没有变化,还是基于原先的逻辑进行编译,导致缺少对应的函数实现,进而导致编译器报警缺少函数实现的错误。
解决
处理方法就是重新qmake一下,也可以修改pro文件进而引发重新生成Makefile。