在逛Qt官方社区的时候看到这样一个帖子:
https://forum.qt.io/topic/117973/how-does-include-moc_-cpp-work
大概的意思是moc_xxx.cpp如果已经被编译器编译,那么在另一个cpp文件中include同一个moc_xxx.cpp应该出现符号冲突才对,但是Qt却能正确运行。对于这个问题,@KH-219Design的回答是:
qmake 负责处理这个场景的编译问题。它会扫描cpp代码和 *.pro 文件以生成 Makefile。如果在编译器和链接器遵循在Makefile中包含的 “Automatic MOC”时,当在源代码中没有找到 #include “moc_myclass.cpp” 时,会修改makefile以包含moc_myclass.cpp:
*.o 文件:
> SOURCES = ../src/main.cpp \
> ../src/myclass.cpp moc_myclass.cpp
> OBJECTS = main.o \
> myclass.o \
> **moc_myclass.o**
当#include 了 moc 文件时,qmake 会扫描到,并相应地调整 Makefile,移除moc_myclass.cpp:
> SOURCES = ../src/main.cpp \
> ../src/myclass.cpp OBJECTS = main.o \
> myclass.o
可见qmake非常智能。
笔者使用cmake的工程对qmake的这个特性进行验证,发现其设计也更为巧妙。
首先,在使用了Q_OBJECT类的源码中,增加一行:
在CMake的Qt项目中,qmake会将一个工程的所有moc_xxx.cpp放在同一个文件中包含,例如:
XXX_autogen\mocs_compilation_Release.cpp,这个文件内容如下:
这样的好处是在新增Q_OBJECT类的时候,不需要修改cmakelist或者改变构建工具的行为。
接下来,我们对比//#include <moc_xxCardObjBase.cpp>
注释前和注释后的区别。发现如果在源码直接包含moc_xxCardObjBase.cpp时,mocs_compilation_Release.cpp文件内容确实少了 include <2FELSIXJDY/moc_xxCardObjBase.cpp>。所以qmake确实会扫描源文件中,include<moc_xxx.cpp>的行为!!
这个过程中,注意到XXX_autogen\include_Debug下新生成了moc_XXCardObjBase.cpp,这个文件和之前生成的moc_XXCardObjBase.cpp.d目录不一样,之前生成的在2FELSIXJDY子目录下。
出于好奇也对比了这两个文件的内容,对比了moc_XXCardObjBase.cpp与2FELSIXJDY/moc_XXCardObjBase.cpp两个文件,发现其内容几乎一模一样,只有头文件的include路径变了。因为这个头文件采用的是相对路径,如果不变,会导致编译不过。
所以,假如我们自作聪明,直接在cpp里写#include <2FELSIXJDY/moc_XXCardObjBase.cpp>,那么反而会遇到路径错误,试了一下,果然如此。
更进一步
想要进一步了解Qt MOC的技术细节,请移步:《探究Qt5【元对象编译器,moc】的 原理和技术细节》