今天被 Qt的一个编译错误弄得心烦透了。原来我想写如下结构的代码。
#include <iostream>
#include <QtCore>
#include <QApplication>
using namespace std;
class Manager:public QObject
{
Q_OBJECT
public:
Manager(QObject *parent = 0);
private slots:
void doSomething();
signals:
void happenSomething();
};
Manager::Manager(QObject *parent): QObject(parent){}
void Manager::doSomething()
{
//doSomething
}
int main()
{
return 0;
}
这样出现了
/home/gikieng/oop/qt_test/QOBJECT/main.cpp:18: error: undefined reference to `vtable for Manager'
这样的错误,上网搜了一下,有人说在在cpp文件中,宏Q_OBJECT不给展开。好吧,我另外用一个.h文件把那些类定义装起来。
#ifndef MANAGER_H
#define MANAGER_H
#include <QtCore>
#include <QApplication>
class Manager:public QObject
{
Q_OBJECT
public:
Manager(QObject *parent = 0);
private slots:
void doSomething();
signals:
void happenSomething();
};
Manager::Manager(QObject *parent): QObject(parent){}
void Manager::doSomething()
{
//doSomething
}
#endif // MANAGER_H
然后又出现了这个奇皅错误:
/home/gikieng/oop/qt_test/QOBJECT/manager.h:21: error: multiple definition of `Manager::doSomething()'
重复定义。。。
我明明上面是声明,下面是定义,怎么会出现重复定义的错误。
然后又是上网狂搜,都是说重复引用头文件引起的,可是我也加#ifndef 进行保护了。
查了一下宏Q_OBJECT。终于有点想法了。
只有在类里面加入了Q_OBJECT宏,才能使用slots和signals。
在预编译的过程中会有一个manager.moc文件的生成,这个应该是Q_OBJECT的展开,想到之前写的声明某个slot而没有进行定义slot时,会可以看到一个文件。
里面有这个么段:
void Manager::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
Manager *_t = static_cast<Manager *>(_o);
switch (_id) {
case 0: _t->happenSomething(); break;
case 1: _t->doSomething(); break;
default: ;
}
} else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast<int *>(_a[0]);
void **func = reinterpret_cast<void **>(_a[1]);
{
typedef void (Manager::*_t)();
if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&Manager::happenSomething)) {
*result = 0;
}
}
}
Q_UNUSED(_a);
}
难道对应一个头文件都会展开一个moc文件,在moc中认不出定义和声明了?
我尝试把所有类外部定义都放入了类内定义。
这下没有出错了。
在header文件中声明类加入Q_OBJECT, 在cpp文件中定义。
也没有出错。
这里面涉及到元对象的技术。明天再看一下。
应该注意:
用到Q_OBJECT宏的类不应该放到cpp文件中声明和定义。
应该在.h文件中声明,然后在.cpp文件中定义。