【问题标题】:Define a QObject derived class inside an anonymous namespace?在匿名命名空间内定义 QObject 派生类?
【发布时间】:2017-05-27 14:38:09
【问题描述】:

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

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

但是,如果实用程序类派生自 Qt 类,则会出现链接错误。我认为问题出在 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 `anonymous namespace'::CUtility::metaObject(void)const” (?metaObject@CUtility@?A0x27a8253c@@UEBAPEBUQMetaObject@@XZ)

LNK2001:未解析的外部符号“public: virtual void * __cdecl `anonymous namespace'::CUtility::qt_metacast(char const *)” (?qt_metacast@CUtility@?A0x27a8253c@@UEAAPEAXPEBD@Z) 罪解析器

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

【问题讨论】:

  • 你有没有尝试过CMyClass.cpp末尾通常的#include "CMyClass.moc",然后重新运行qmake+make?

标签: c++ qt


【解决方案1】:

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

回想一下,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"

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 谢谢。但是,如果我想拥有相同的实用程序类,我该如何限制它的范围?我的意思是,我的类 CUtility 将仅由 CMyClass 使用,我不想生成另一对文件。如果它只是一个函数,我认为我会使用一个静态函数,但是对于一个类是不可能的。有什么建议吗?
    • 这个答案是错误的。 Qt 代码充满了Q_OBJECT 实用程序类。回想一下 qmake/cmake 中的 Qt 构建支持:它将 moc 输出转储到 .cpp 文件中,然后构建它。您可以轻松地将 moc 输出添加到现有的 .cpp 文件中!
    • @KubaOber 只有 1 句话是错误的。但实际上我错过了包含 moc cpp 文件的主要解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-22
    • 1970-01-01
    • 1970-01-01
    • 2011-12-18
    • 2011-05-10
    • 1970-01-01
    相关资源
    最近更新 更多