简体   繁体   English

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

[英]Define a QObject derived class inside an anonymous namespace?

I am working with Qt 5.7 (C++).我正在使用 Qt 5.7 (C++)。

Inside the cpp file of one class I am using an anonymous namespace to create a class (some utility) that I will use only in that file.在一个类的 cpp 文件中,我使用匿名命名空间来创建一个仅在该文件中使用的类(某些实用程序)。

However, I got Linking errors if the utility-class is derived from a Qt class.但是,如果实用程序类派生自 Qt 类,则会出现链接错误。 I think that the problem is at the Q_OBJECT macro, if I don't add it I don't get the errors.我认为问题出在 Q_OBJECT 宏上,如果我不添加它,我就不会收到错误消息。 But in any Qt derived class is imperative/recommended to have the Q_OBJECT macro.但是在任何 Qt 派生类中都必须/推荐使用 Q_OBJECT 宏。

How can I avoid this isue?我怎样才能避免这个问题? Is there any other approach to have a utility-class with file-scope?有没有其他方法可以让实用程序类具有文件范围?

Simple example to show errors: the class CMyClass uses a utility class (named CUtility) that derives from QWidget.显示错误的简单示例:类 CMyClass 使用派生自 QWidget 的实用程序类(名为 CUtility)。

Thank you.谢谢你。

CMyClass.h CMyClass.h

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

CMyClass.cpp 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();
}

The errors are:错误是:

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

LNK2001: unresolved external symbol "public: virtual void * __cdecl `anonymous namespace'::CUtility::qt_metacast(char const *)" (?qt_metacast@CUtility@?A0x27a8253c@@UEAAPEAXPEBD@Z) sin resolver LNK2001:未解析的外部符号“public: virtual void * __cdecl `匿名命名空间'::CUtility::qt_metacast(char const *)” (?qt_metacast@CUtility@?A0x27a8253c@@UEAAPEAXPEBD@Z) 罪解析器

LNK2001: unresolved external symbol "public: virtual int __cdecl `anonymous namespace'::CUtility::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@CUtility@?A0x27a8253c@@UEAAHW4Call@QMetaObject@@HPEAPEAX@Z) sin resolver LNK2001:未解析的外部符号“public: virtual int __cdecl `匿名命名空间'::CUtility::qt_metacall(enum QMetaObject::Call,int,void * *)” (?qt_metacall@CUtility@?A0x27a8253c@@UEAAHW4Call@QMetaObject@@ HPEAPEAX@Z) sin 解析器

This has nothing to do with anonymous namespaces at all.这与匿名命名空间完全无关。 They are a non sequitur, in fact.事实上,它们是不合逻辑的。

Recall that moc generates the implementations of a few methods, including signals, and some static data.回想一下, moc 生成了一些方法的实现,包括信号和一些静态数据。 For this to work, the class declaration must be visible to moc output.为此,类声明必须对 moc 输出可见。 It is visible at the end of the .cpp file.它在.cpp文件的末尾可见。

Thus, to have a Q_OBJECT class inside a foo.cpp file, you must #include "foo.moc" at the end of that file.因此,要在foo.cpp文件中包含Q_OBJECT类,您必须在该文件的末尾#include "foo.moc" Then just re-build if using cmake, or, for qmake, re-run qmake first and then build the project.如果使用 cmake,则只需重新构建,或者,对于 qmake,首先重新运行 qmake,然后构建项目。 That's all.就这样。

In the complete example below, the Utility class can be in the anonymous namespace, but doesn't have to be.在下面的完整示例中, Utility类可以位于匿名命名空间中,但并非必须如此。 The anonymous namespace isn't "really" a namespace: it has a special meaning that limits the scope of the contained identifiers to the translation unit.匿名命名空间并不是“真正的”命名空间:它具有特殊的含义,将包含的标识符的范围限制为翻译单元。 It's like static , except it can be also applied to types, not only functions and variables.它就像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"

It doesn't work with the Q_OBJECT macro because the macro add members to your class, members which are defined in the C++ code generated by the moc (generally in moc_CMyClass.cpp making it incompatible with a file-scope).它不适用于Q_OBJECT宏,因为该宏将成员添加到您的类中,这些成员在 moc 生成的 C++ 代码中定义(通常在moc_CMyClass.cpp ,使其与文件范围不兼容)。

One possible solution is to skip the Q_OBJECT macro, it is not mandatory and you may not need it.一种可能的解决方案是跳过Q_OBJECT宏,它不是强制性的,您可能不需要它。 The drawback is that you will lose introspection information about your class and cannot declare signals and slots.缺点是您将丢失有关您的类的内省信息,并且无法声明信号和槽。

The other solution is, as suggested by @KubaOber, to include the generated cpp file at the end of your own copy file.另一种解决方案是,正如@KubaOber 所建议的那样,将生成的 cpp 文件包含在您自己的副本文件的末尾。 In this case qmake will detect it and will not compile the moc cpp file on its own.在这种情况下, qmake将检测到它并且不会自行编译 moc cpp 文件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM