Q_OBJECT宏是干什么的?为什么所有Qt对象都需要这个宏?

152
我刚开始使用Qt,注意到所有示例类的定义都有宏Q_OBJECT作为第一行。这个预处理器宏的目的是什么?
6个回答

145

来自Qt文档:

元对象编译器(moc)是处理Qt的C++扩展的程序。

moc工具读取C++头文件。如果它发现一个或多个包含Q_OBJECT宏的类声明,它将生成一个C++源文件,其中包含这些类的元对象代码。除其他外,元对象代码需要用于信号和槽机制、运行时类型信息和动态属性系统。


为什么我不需要显式地编写 Q_OBJECT::connect(),而只需要编写 connect() - mLstudent33
2
@mLstudent33 如果你想的话,可以写QObject::connect()。 - Patapoom

23

这段代码告诉预编译器,该类需要通过'moc'或Meta-Object Compiler运行,moc会为该类添加额外的隐藏字段和函数,并解析信号和槽。只有使用信号/槽机制或其他Qt类级功能(例如内省)的类才需要添加此代码。对于仅使用标准C++功能的类,无需添加Q_OBJECT。


4
只在使用信号/槽机制的类上加 Q_OBJECT 是错误的。缺少 Q_OBJECT 会破坏 qobject_cast 和反射(introspection)功能,这可能导致一些混乱的行为,因此这是一个不好的想法。 - Kuba hasn't forgotten Monica
4
在其他非QObject类中,“Q_OBJECT”不会被“悄悄地”忽略,这是不正确的说法。根据C++标准,它通过声明一些永远不会被定义的成员函数和变量引入了未定义的行为。它还将您的类命名空间污染为QObject特定成员。例如,一个Q_OBJECT可能会破坏一个包含名为“metaObject”的方法的无关类。 - Kuba hasn't forgotten Monica
2
那是错误的。虽然你可能想要为大多数GUI类配备Q_OBJECT宏,但是拥有宏的非GUI类以及没有宏的GUI类也是完全有意义的。该宏很有用,但不仅限于GUI类,也不是必需的。 - pasbi

8

MOC (元对象编译器)将包含 Q_OBJECT 宏的头文件转换为C++等效源代码。它主要控制信号槽机制,并使其对C++编译器可读。


2
那是错误的:Q_OBJECT 宏是由编译器展开的,不需要 moc。moc 不会对宏本身进行任何操作,但它会生成 Q_OBJECT 宏所声明的成员变量和方法的定义 - Kuba hasn't forgotten Monica

5

1 来自Qt文档的元对象系统

moc工具读取C++源文件。如果它发现一个或多个类声明包含Q_OBJECT宏,它将为每个类生成元对象代码的另一个C++源文件。此生成的源文件要么被#include到类的源文件中,要么更常见的是与类的实现一起编译和链接。

2 来自Qt文档的Q_OBJECT

Q_OBJECT宏必须出现在类定义的私有部分中,该类声明其自己的信号和槽,或使用Qt元对象系统提供的其他服务。

3 来自Qt文档的moc

The moc工具读取C++头文件。如果它找到一个或多个包含Q_OBJECT宏的类声明,它将生成一个包含这些类的元对象代码的C++源文件。元对象代码除了其他功能外,还需要用于信号和槽机制、运行时类型信息和动态属性系统。
从Qt文档4的Signals and Slots
Q_OBJECT宏由预处理器扩展为声明几个成员函数,这些函数由moc实现;如果您得到类似“undefined reference to vtable for LcdNumber”的编译器错误,则可能忘记运行moc或在链接命令中包含moc输出。

2

在gcc中,使用-E可以查看扩展的宏。这是在Linux上gcc中Q_OBJECT扩展成的内容。请注意,这可能与平台相关,并且可能会根据QT版本而更改。您可以看到它不仅仅是moc编译器的标签。

# 11 "mainwindow.hh"
#pragma GCC diagnostic push
# 11 "mainwindow.hh"

# 11 "mainwindow.hh"
#pragma GCC diagnostic ignored "-Wsuggest-override"
# 11 "mainwindow.hh"
    static const QMetaObject staticMetaObject; virtual const QMetaObject *metaObject() const; virtual void *qt_metacast(const char *); virtual int qt_metacall(QMetaObject::Call, int, void **); static inline QString tr(const char *s, cons
t char *c = nullptr, int n = -1) { return staticMetaObject.tr(s, c, n); } __attribute__ ((__deprecated__)) static inline QString trUtf8(const char *s, const char *c = nullptr, int n = -1) { return staticMetaObject.tr(s, c, n); } private:
# 11 "mainwindow.hh"
#pragma GCC diagnostic ignored "-Wattributes"
# 11 "mainwindow.hh"
    __attribute__((visibility("hidden"))) static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
# 11 "mainwindow.hh"
#pragma GCC diagnostic pop
# 11 "mainwindow.hh"
    struct QPrivateSignal {};

0

Q_OBJECT宏必须出现在类定义的私有部分中,该类定义声明其自己的信号和槽或使用Qt元对象系统提供的其他服务。


2
这是误导性的:每个继承自QObject的类都必须出现Q_OBJECT宏。当该宏缺失时,您的代码将会出现微妙的错误,仅仅因为它编译通过并不代表它是正确的。 - Kuba hasn't forgotten Monica
@KubaOber,你有没有一个代码示例,在缺少Q_OBJECT宏的情况下,它可以编译但不起作用? - Chris
2
如果你查看Q_OBJECT的实现,你会发现它使用了访问说明符。因此,无论这个宏出现在privateprotected还是public说明符下都是无关紧要的——把它放在类的开头只是一种惯例。 - TrebledJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接