Separate class into header and source for Qt projects

I used to putting all my code in one .cpp file. That is my style. Don’t be fooled by the “principles” and “theories” advocated by software engineering experts. Those “principles” and “theories” are mainly for massive software development done by many people. For individual developers, using a single .cpp is almost the best practice. I once put nearly 100,000 lines of code in a single source file and that works like a charm.

But some day, you may want to take advantage of the project management of Qt creator, i.e., you may want to play with the .pro file, qmake, etc. I personally seldom touched those stuff in the past, and this is why. In that case, you may encounter some problems. For example, you may encounter the following errors:

error LNK2001: unresolved external symbol “public: virtual struct QMetaObject const * __thiscall Dialog::metaObject(void)const ”

error LNK2001: unresolved external symbol “public: virtual void * __thiscall Dialog::qt_metacast(char const *)”

error LNK2001: unresolved external symbol “public: virtual int __thiscall Dialog::qt_metacall(enum QMetaObject::Call,int,void * *)”

This is because you used the Q_OBJECT macro in your class and did not generate the .moc file for the class. If you did not use the .pro file to manage the project, you could manually generate the moc file using moc yoursinglesource.cpp > yoursinglesource.moc, and include yoursinglesource.moc in yoursinglesource.cpp. If you use a .pro file to manage your source code, you can still use the same method, no need to fill yoursinglesource.moc in the .pro file, no need to separate the class into header and source. But since Qt creator can automatically generate moc files for us, you may want to take advantage of that instead of manually generating it.

However, Qt creator only automatically generates moc files for header files. So, if you put both the declaration/definition and the implementation of class in source file, Qt creator won’t generate the moc file for the class, and you still need to generate it yourself, then include the generated .moc file in the source file.

To let Qt creator produce the moc file for your class, you should put the definition/declaration of the class in a header file. Of course, you can choose to include the implementation of the class in the header file as well  but more commonly the implementation of the class is separated into another source file. Qt creator will automatically generates a .cpp file moc_header.cpp for the header file header.h. This is an independent compiling unit which means to successfully compile the cpp file, the cpp file needs to include the header file as well, which also means you cannot include the definition of global functions/variables in the header file, otherwise, it will produce the re-definition error at linking time because your main source file also includes the header file. So you will have to remove the global functions/variables from the header file to another source file. But what if the implementation of the class in the header file uses those global functions/variables? You can declare(not define) those globals in the header file to make the compiler happy when compiling moc_header.cpp and yoursinglesource.cpp. And the linking won’t give you the multiple definitions error since there is only one version of these globals. Alternatively, you can remove the implementation part of the class from the header file and put them in another source file. In this case, you do not need to declare the globals in the header file and this is why it is the method that is commonly used.

To summarize, to utilize the automatic moc generation mechanism of  qt creator, you’d better separate the declaration and the implementation of your QObject derived  class into two files: a header file and a source file. Do not try to implement the class in the header file. Qt creator does not actually generates the .moc file but an ordinary .cpp file with the same content. Qt won’t include the .cpp file in your own source file but link to the generated .cpp file.

Posted in

Leave a Reply