我有一个Qt应用程序,它有多个使用信号和插槽的类,它编译得很好。但是,当我在主CPP(main.cpp)文件中创建自定义类时,我收到链接器错误。
以下是我使用的代码:
class Counter : public QObject
{
Q_OBJECT
public:
Counter() { m_value = 0; }
int value() const { return m_value; }
public slots:
void setValue(int value)
{
if(value!=m_value)
{
m_value = value;
qDebug() << "Value " << value;
emit valueChanged(value);
}
}
signals:
void valueChanged(int newValue);
private:
int m_value;
};
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Counter a, b;
QObject::connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));
a.setValue(12); // a.value() == 12, b.value() == 12
b.setValue(48); // a.value() == 12, b.value() == 48
QTimer::singleShot(0, &app, SLOT(quit()));
return app.exec();
}
以下是错误:
Error 4 error LNK2001: unresolved external symbol "public: virtual struct QMetaObject const * __thiscall Counter::metaObject(void)const " (?metaObject@Counter@@UBEPBUQMetaObject@@XZ)
Error 5 error LNK2001: unresolved external symbol "public: virtual void * __thiscall Counter::qt_metacast(char const *)" (?qt_metacast@Counter@@UAEPAXPBD@Z)
Error 6 error LNK2001: unresolved external symbol "public: virtual int __thiscall Counter::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@Counter@@UAEHW4Call@QMetaObject@@HPAPAX@Z)
Error 7 error LNK2019: unresolved external symbol "protected: void __thiscall Counter::valueChanged(int)" (?valueChanged@Counter@@IAEXH@Z) referenced in function "public: void __thiscall Counter::setValue(int)" (?setValue@Counter@@QAEXH@Z)
将计数器放在单独的头文件中时,不会发生此链接器错误。这种行为的原因是什么?
答案 0 :(得分:5)
我假设您正在使用qmake
。
默认情况下,moc
会自动在头文件上运行,因为这是一般声明类的地方。请注意,此规则在makefile中定义,您可以在源文件上手动运行moc
。
您必须通知qmake
该文件包含一个类。为此,请在#include "filename.moc"
声明后添加Counter
。您可以查看更多详细信息here (QtCentre)或here (doc)。
如果您正在使用除qmake
之外的其他工具,比如CMake,您必须指定一条规则来强制moc解析.cpp
文件(最简单的是处理所有文件)。对于不包含Qt对象类的文件,moc
将生成一个空文件。
但是,即使将此类设为“私有”,我建议您在标题中声明它(例如counter_private.h
)。例如,Qt源正在使用这个技巧。
答案 1 :(得分:2)
看起来你只有一个代码文件。如果使用默认方式创建Qt项目构建(qmake&amp;&amp; make或QtCreator),则MOC仅扫描* .h文件。如果你将所有代码放在一个main.cpp中,MOC将不会创建任何代码,但信号/插槽需要这些代码才能工作。
使这个特定示例工作的最简单方法是在main.cpp的末尾添加一行“#include”main.moc“”。 将检测此依赖关系,并将创建所需的Makefile目标。
最可爱的方式是干净的:一个类 - 一个标题和一个实现文件。
答案 2 :(得分:1)
他们的moc / uic自定义构建命令是在头文件上完成的,因此它在放入单独的头文件/源文件时编译,而不是在放入相同的源文件时编译