在匿名命名空间中定义QObject派生类

Define a QObject derived class inside an anonymous namespace?

本文关键字:QObject 派生 定义 命名空间      更新时间:2023-10-16

我正在使用Qt 5.7(C++)。

在一个类的cpp文件中,我使用一个匿名命名空间来创建一个类(一些实用程序),我将只在该文件中使用该类。

但是,如果实用程序类是从Qt类派生的,则会出现Linking错误。我认为问题出在Q_OBJECT宏上,如果我不添加它,我就不会得到错误。但在任何Qt派生类中,都必须/建议使用Q_OBJECT宏。

我该如何避免这种情况?是否有其他方法可以创建具有文件作用域的实用程序类

显示错误的简单示例:类CMyClass使用从QWidget派生的实用程序类(名为CUtility)。

谢谢。

CMyClass.h

class CMyClass
{
public:
CMyClass();
void someMethod();
};

CMyClass.cpp

#include <QtWidgets>
#include "CMyClass.h"
namespace
{
class CUtility : public QWidget
{
Q_OBJECT
public:
CUtility(QWidget *p_parent = 0) : QWidget(p_parent){qDebug() << "CUtility constructor";}
void utilityMethod() {qDebug() << "This is CUtility::utilityMethod()";}
};
}

CMyClass::CMyClass()
{
qDebug() << "CMyClass constructor.";
}
void CMyClass::someMethod()
{
qDebug() << "This is CMyClass::someMethod().";
CUtility p_myUtil;
p_myUtil.utilityMethod();
}

错误为:

LNK2001:未解析的外部符号"public:virtual struct QMetaObject const*__cdecl`匿名命名空间'::CUtility::metaObject(void)const"(?metaObject@CUtility@?A0x27a8253c@@UEBAPEBUQMetaObject@@XZ)

LNK2001:未解析的外部符号"public:virtual void*__cdecl`匿名命名空间'::实用性:qt_metacast(char const*)"(?qt_metacast@CUtility@?A0x27a8253c@@UEAAPEAXPEBD@Z)sin分解器

LNK2001:未解析的外部符号"public:virtual int __cdecl`anonynamespace'::CUtility::qt_metacall(enum QMetaObject::Call,int,void**)"(?qt_metacall@CUtility@?A0x27a8253c@@UEAAHW4Call@QMetaObject@@HPEAPEAX@Z)sin分解器

这与匿名命名空间完全无关。事实上,它们是不合逻辑的。

回想一下,moc生成了一些方法的实现,包括信号和一些静态数据。为了实现这一点,类声明必须对moc输出可见。它在.cpp文件的末尾可见。

因此,要在foo.cpp文件中有一个Q_OBJECT类,必须在该文件的末尾有#include "foo.moc"。如果使用cmake,则只需重新构建,或者,对于qmake,首先重新运行qmake然后构建项目。仅此而已。

在下面的完整示例中,Utility类可以在匿名命名空间中,但不一定要在;真的";名称空间:它有一个特殊的含义,将包含的标识符的范围限制为翻译单元。它类似于static,只是它也可以应用于类型,而不仅仅是函数和变量。

// main.cpp
#include <QObject>
namespace {
class Utility : public QObject {
Q_OBJECT
public:
Utility(QObject *parent = {});
};
}
Utility::Utility(QObject *parent) : QObject(parent) {}
int main() {
Utility utility;
}
#include "main.moc"

它不适用于Q_OBJECT宏,因为宏会向类添加成员,这些成员在moc生成的C++代码中定义(通常在moc_CMyClass.cpp中,使其与文件范围不兼容)。

一种可能的解决方案是跳过Q_OBJECT宏,它不是强制性的,你可能不需要它。缺点是你会丢失关于你的类的内省信息,并且不能声明信号和槽。

另一种解决方案是,正如@KubaOber所建议的,在您自己的副本文件的末尾包含生成的cpp文件。在这种情况下,qmake将检测到它,并且不会编译moccpp文件本身。