[QT] Meta-object system study notes (1)

01. Meta-object system

The meta-object system is some extensions of Qt to the original C++. It is mainly introduced to implement the signal and slot mechanism. The signal and slot mechanism is the core feature of Qt.

The functions provided by Qt's meta-object system are:
1. Signal and slot mechanism for communication between objects
2. Runtime type information and dynamic attribute system, etc.

To use the functionality of the meta-object system, three prerequisites need to be met.

  1. This class must inherit from the QObject class.
  2. A macro must be added in the private area of ​​the class declaration Q_OBJECT, which is used to enable meta-object properties, and then you can use functions such as dynamic properties, signals and slots.
  3. The meta-object compiler (moc) provides each QObject subclass with the code necessary to implement the meta-object features.
1.1. Meta-object operating principles
  • Because the meta-object system is an extension of C++, Qt programs with the meta-object system enabled cannot be directly compiled using traditional compilers. Before compiling the Qt program, the extended syntax needs to be removed. This function is the moc function. Things to do.
  • The full name of moc is: Meta-Object Compiler. It is a tool (similar to qmake). This tool reads and analyzes C++ source files. If one or more class declarations containing macros are found Q_OBJECT, Then another Q_OBJECTC++ source file containing the macro implementation code will be generated (the source file is usually named: moc_*.cpp). This new source file will either be #included into the source file of the class, or compiled and decrypted. into the class's implementation (usually this approach is used). Note: The new file will not "replace" the old file, but the same source files will be compiled together.

Other concepts:

1. Meta-object code: refers to the code of the source file generated by the moc tool, which contains the Q_OBJECTmacro implementation code
. 2. The path of the moc tool is generally in the installation path, such as: C:\app\Qt5.8.0MinGw\5.8 \mingw53_32\bin

1.2, Q_OBJECT Hiroshi

First look at a picture, as long as it contains Q_OBJECTmacros, and then F2, you can enter.
Insert image description here
It can be seen that Q_OBJECTthe macro adds some members to the declared class, and the red boxes mark the virtual function members (note that these virtual functions are not defined). According to C++ syntax, virtual functions must be defined or declared as pure virtual functions, moc tool One of its jobs is to generate the definitions of the above members, and also generate some other necessary code.

1.3. Qt Creator starts the meta-object system

Since the moc tool is used through Qt Creator, it must be ensured that moc can discover and process Q_OBJECTclasses containing macros in the project. Therefore, the following rules need to be followed:
1. The definition of classes containing macros derived from QObject Q_OBJECTmust be in the header file 2.
Make sure prothat all source files (SOURCES variable) and header files (HEADERS variable) in the project are listed in the file;
3. Logic instructions such as ( ) should be used in the header file #ifndef、#define、#endifto prevent header files from being included multiple times.
4. The QObject class should be the first class in the base class list.

It can be seen from the above rules that when writing code using Qt Creator, the class should be defined in the header file, and the
definition of the member function should be located in the source file (this can avoid redefinition errors caused by including the header file multiple times). Although the
program is written in this way It's cumbersome, but it's a good way to organize your code.

Note: If a derived class of the QObject class is defined and built (the build is not added O_OBJECT, but is added after the build is completed), the qmake command must be executed again at this time, otherwise moc cannot generate code.

Here are the original words from the book: Run
Insert image description here
the above program and you can find a moc_m.cpp source file in the debug directory. This source file is
generated using the moc tool. The code in the source file is the meta-object code. Readers can view it. its code. If there is no
moc_m.cpp file in this directory, it means that the moc tool failed to start normally. In this case, you need to execute the qmake command in Qt Creator and
then build the program.

1.4. Command line startup meta object (not commonly used)
  1. You need to use the moc tool on the command line, and include the cpp file generated by the moc tool in the source file.
  2. At this time, the class containing Q_OBJECT does not need to be located in the header file. Assuming it is located in the m.cpp file, the content is:
#include<QObject>
class A : public QObject
{
    
      
	Q_OBJECT
public:
  A(){
    
    }  
};

int main(int argc, char *argv[]) {
    
    
 	A ma; return 0; 
}
  1. Open the Qt 5.8 for Desktop (MinGW 5.3.0 32 bit) command line tool and enter the following command:
moc d:\qt\m.cpp -o d:\qt\mm.cpp

The above command will generate the mm.cpp file based on m.cpp. The code in the mm.cpp file is the meta-object code. Here,
m.cpp and mm.cpp are both located in the d:\qt folder.

  1. Then open m.cpp again and use #include to include mm.cpp, as follows:
#include <QObject>
//#include "mm.cpp"   // 不能把mm.cpp包含在类A的定义之前
class A : public QObject {
    
    
	Q_OBJECT
public:
	A(){
    
    }
};

#include "mm.cpp"  // 必须把mm.cpp包含在类A的定义后面,因为mm.cpp源文件中有对A的成员定义,此时必须见到类A的完整定义。

int main(int argc, char* argv[]) {
    
    
	A ma;
	return 0;
}
  1. Then use qmake to generate the project file and makefile file, and then use the mingw32-make command.

Generally speaking, QT developers rarely use the command line method, because Qt Creator has already done this for us. I just saw it mentioned in the book and recorded it here.

02. Reflection mechanism

Reflection mode (reflection mode/reflection mechanism): refers to a mechanism that can obtain all type information, attributes, member functions and other information of any class object at runtime.

Meta-object system and reflection mechanism:
1. One of the functions provided by the meta-object system is to provide runtime type information and current values ​​of data
members In other words, during the running phase, the program can obtain
The name of the class to which the QObject derived class object belongs , the name of the parent class, the object's member functions, enumeration types, data members and other information. In fact, this is
the reflection mechanism.

2. Because Qt's meta-object system must inherit from QObject, and the main role of the reflection mechanism can be seen, Qt's
meta-object system mainly provides the program with information about QObject class objects and their derived class objects, that is to say, it does not
is a class object derived from QObject, you cannot use Qt's meta-object system to obtain this information.

The specific explanation of meta-object: refers to an object used to describe the structure of another object.

Concretely translated into a program as follows:

class B
{
    
    
	//TODO....
};

class A : public B
{
    
    
	B mb;
};

Assuming that mb is used to describe objects created by class A, then mb is a meta-object.

2.1. Qt implements reflection mechanism
  1. Qt uses a series of classes to implement the reflection mechanism. These classes describe each side of the object. The class QMetaObjectdescribes all the meta-information of QObject and its derived class objects. This class is the core class of the Qt meta-object system. Through The member functions of this class can obtain all meta-information of QObject and its derived class objects, so it can be said that the QMetaObjectobjects of the class are meta-objects in Qt. Note: To call QMetaObjectmember functions in a class, you need to use QMetaObjectan object of the type.
  2. Describe the members of the object: An object contains data members, function members, constructors, enumeration members, etc. In Qt, these members are described using different classes. For example, function members are described using classes QMetaMethodand attributes . QMetaPropertyFinally, use the QMetaObject class to describe the entire class object, for example, to obtain the variable name of the member function:
QMetaMethod qm = metaObject->method(1);  // 获取索引为1的成员函数
qDebug()<< qm.name() << "\n";  // 输出该成员函数的名称

Prerequisites for using Qt's reflection mechanism:

  1. It needs to be inherited from the QObject class and the Q_OBJECT macro needs to be added to the class.
  2. QObject::Q_INVOKABLERegistering member functions: If you want ordinary member functions to be reflected, you need to add macros before the function declaration .
  3. Register member variables: If you want member variables to be reflected, you need to use Q_PROPERTYmacros.

In fact QObject::Q_INVOKABLE, Q_PROPERTYI have a deep understanding of these two macros in QQuick in Qt5. If QML wants to use the properties and methods of C++, these macros need to be marked.

Brief description of the implementation principle of Qt reflection mechanism:

  1. Q_OBJECTAfter macro expansion, there is a virtual member function meteObject(), which returns a QMetaObjectpointer to the type, and its prototype is:
virtual const QMetaObject* metaObject() const;

Because the classes that enable the meta-object system all contain Q_OBJECTmacros, these classes all contain virtual member functions. By calling the member functions in the class metaObject()through the pointer returned by the function , you can query various information about QObject and its derived class objects.QMetaObject

  1. Qt's moc will complete the following work:
    1. Q_OBJECTGenerate implementation code for the member functions declared after macro expansion;
    2. Identify special keywords and macros in Qt, such as identifying Q_PROPRTY, Q_INVOKABLE, slot, signalsetc.
2.2. Reflection mechanism to obtain information about member functions in a class
2.1.1, QMetaMethon class

Function: Used to describe the member functions of an object. You can use the member functions of this class to obtain information about the object's member functions.

List some commonly used members:

// 此枚举用于描述函数的类型,即:普通成员函数、信号、槽、构造函数
enum MethodType {
    
    Method, Signal, Slot, Constructor}

// 此枚举主要用于描述函数的访问级别(私有的、受保护的、公有的)
enum Access {
    
    Private,Protected,Public}

// 返回函数的签名(qt5.0)
QByteArray methodSignature() const;

// 返回函数的类型(信号、槽、成员函数、构造函数)
MethodType methodType() const;

// 返回函数的名称(qt5.0)
QByteArray name() const;

// 返回函数的参数数量(qt5.0)
int parameterCount() const;

// 返回函数参数名称的列表
QList<QByteArray> parameterNames() const;

// 返回指定索引处的参数类型,返回值是使用QMetaType注册的类型,若类型未注册,则返回值为QMetaType::UnknownType。
int parameterType(int index) const;

// 返回函数参数类型的列表
QList<QByteArray> parameterTypes() const;

// 返回函数的返回类型。返回值是使用QMetaType注册的类型,若类型未注册,则返回值为QMetaType::UnknownType。
int returnType() const;

// 返回函数的返回类型的名称
const char* typeName() const;

// 返回函数的访问级别(私有的、受保护的、公有的)
Access access() const;
2.1.2. QMetaObject class

Function: Used to provide meta-object information about the class.

List some commonly used members:

/* 返回名为 f 的函数的索引号,否则返回-1。此处应输入正确的函数签名,比如函数形
式为 void f(int i,int j);则正确的形式为 xx.indexOfMethod("f(int,int"); 以下形式都不是
正确的形式,"f(int i, int j)"、"void f(int, int)"、 "f"、"void f"等。*/
int indexOfMethod(const char* f) const;

// 返回信号 s 的索引号,否则返回-1,若指定的函数存在,但不是信号,仍返回-1。
int indexOfSignal(const char * s) const;

// 返回构造函数 c 的索引号,否则返回-1
int indexOfConstructor(const char *c) const;

// 返回构造函数的数量。
int constructorCount() const ; 

// 返回指定索引 i 处的构造函数的元数据。
QMetaMethod constructor(int i)const;

// 返回函数的数量,包括基类中的函数、信号、槽和普通成员函数。
int methodCount() const;

// 返回父类中的所有函数的总和,也就是说返回的值是该类中的第一个成员函数的索引位置。
int methodOffset() const;

// 返回指定索引 i 处的函数的元数据。
QMetaMethod method(int i) const;

The following is an example from the book for reference only

#include "m.h"
#include <QMetaMethod>
#include <QByteArray>
#include <iostream>
using namespace std;
int main(){
    
      A ma; B mb; //创建两个对象
const QMetaObject *pa=ma. metaObject ();
const QMetaObject *pb=mb. metaObject ();
//以下为通过 QMetaObject 的成员函数获取的信息。
int j=pa->methodCount(); /*返回对象 ma 中的成员函数数量,包括从父类 QObject 继承而来的 5 个
成员函数及本对象中的 2 个成员函数(注意,不包括 g1)、1 个信号,所以
总数为 8。*/
cout<<j<<endl; //输出 8
int i=pa->indexOfMethod("g(int,float)"); //获取对象 ma 中的成员函数 g 的索引号。
cout<<i<<endl; //输出 7
i=pa->constructorCount(); //获取对象 ma 所属类中的构造函数的数量。
cout<<i<<endl; //输出 2
i=pb->constructorCount(); /*获取对象 mb 所属类 B 中的构造函数的数量,因类 B 无构造函数,所以
返回值为 0,此处也可看到,构造函数数量不包含父类的构造函数*/
cout<<i<<endl; //输出 0。
i=pa->indexOfConstructor("A(int)"); //获取对象 ma 所属类中的构造函数 A(int)的索引号。
cout<<i<<endl; //输出 1。
i=pa->indexOfSignal("gb3()"); //获取对象 ma 的信号 gb3()的索引号。
cout<<i<<endl; //输出 5。
i=pa->indexOfSignal("f()"); /*获取对象 ma 的信号 f()的索引号。因为成员函数 f 存在,但不是信
号,所以返回值为-1。*/
cout<<i<<endl; //输出-1。
i=pb->methodOffset(); /*获取父类的成员函数数量,包括父类A及QObject中的成员函数,总共为8。
*/
cout<<i<<endl; //输出 8,此处相当于是对象 mb 自身成员函数开始处的索引号。
//以下为通过 QMetaMethon 的成员函数获取的信息。
//获取对象 ma 的成员函数 g 的元数据。
QMetaMethod m=pa->method(pa->indexOfMethod("g(int,float)"));
QByteArray s= m.name(); //获取成员函数 g 的函数名。
cout<<s.data()<<endl; //输出 g
s=m.methodSignature(); //获取函数 g 的签名
cout<<s.data()<<endl; //输出 g(int,float)
i=m.methodType(); /*获取函数 g 的类型,此处返回的是 QMetaMethod::MethodType 中定义的枚举值,
其中 Method=0,表示类型为成员函数*/
cout<<i<<endl; //输出 0(表示成员函数)。
//以下信息与函数的返回类型有关
s=m.typeName(); //获取函数 g 的返回值的类型名
cout<<s.data()<<endl; //输出 void
i=m.returnType(); /*获取函数 g 返回值的类型,此处的类型是 QMetaType 中定义的枚举值,其中枚举
值 QMetaType::void=43*/
cout<<i<<endl; //输出 43
//以下信息与函数的参数有关
i=m.parameterType(1); /*获取函数 g 中索引号为 1 的参数类型,此处的类型是 QMetaType 中定义的
枚举值,其中枚举值 QMetaType::float=38*/
cout<<i<<endl; //输出 38
QList<QByteArray> q=m.parameterNames(); //获取函数 g 的参数名列表
cout<<q[0].data()<<q[1].data()<<endl; //输出 ij
q=m.parameterTypes(); //获取函数 g 的参数类型列表。
cout<<q[0].data()<<q[1].data()<<endl; //输出 intfloat
return 0; }
2.3. Reflection mechanism to obtain class-related information
  1. The member functions in the QMetaObject class to obtain class-related information are:
/*获取类的名称,注意,若某个 QObject 的子类未启动元对象系统(即未使用 Q_OBJECT
宏),则该函数将获取与该类最接近的启动了元对象系统的父类的名称,而不再返回
该类的名称,因此建议所有的 QObject 子类都使用 Q_OBJECT 宏。
*/
const char* className() const;

// 返回父类的元对象,若没有这样的对象则返回0
const QMetaObject* superClass() const;

// 若该类继承自mo描述的类型,则返回true,否则返回false。类被认为继承自身。
bool inherits(const QMetaObject* mo) const;  // (Qt5.7)
  1. The member functions in the QObject class to obtain class-related information are:
// 若该类是className指定的类的子类则返回true,否则返回false。类被认为继承自身
bool inherits(const char* className) const;

Here's an example:

head File:

#ifndef M_H //要使用元对象系统,需在头文件中定义类。
#define M_H
#include<QObject>
class A:public QObject{
    
     Q_OBJECT};
class B:public A{
    
     Q_OBJECT};
class C:public QObject{
    
    Q_OBJECT};
class D:public C{
    
    };
#endif // M_H

Source File:

#include "m.h"
#include <QMetaMethod>
#include <iostream>
using namespace std;
int main(){
    
      A ma; B mb; C mc; D md;
const QMetaObject *pa=ma. metaObject ();
const QMetaObject *pb=mb. metaObject ();
cout<<pa->className()<<endl; //输出类名 A
//使用 QMetaObject::inherits()函数判断继承关系。
cout<<pa->inherits(pa)<<endl; //输出 1,类被认为是自身的子类
cout<<pa->inherits(pb)<<endl; //输出 0,由 pb 所描述的类 B 不是类 A 的子类。
cout<<pb->inherits(pa)<<endl; //输出 1,由 pa 所描述的类 A 是类 B 的子类。
//使用 QObject::inherits()函数判断继承关系。
cout<<ma.inherits("B")<<endl; //输出 0,类 A 不是类 B 的子类。
cout<<ma.inherits("A")<<endl; //输出 1,类被认为是自身的子类
cout<<md.inherits("D")<<endl; //输出 0,因为类 D 未启动元对象系统。
cout<<md.inherits("C")<<endl; /*输出 1,虽然类 D 未启动元对象系统,但类 C 已启动,此种情形下
能正确判断继承关系。*/
cout<<md. metaObject ()->className()<<endl; /*输出 C,此处未输出 D,因为类 D 未启动元对象系统,
与类 D 最接近的启动了元对象系统的父类是 C,因此返回 C。*/
return 0; }
  1. The qobject_cast function uses the following syntax:
DestType* qobject_cast<DestType*>(QObject* p);

1. This function is similar to dynamic_cast in C++, but its execution speed is faster than dynamic_cast and does not require the
support of C++'s RTTI. However, qobject_cast is only applicable to QObject and its derived classes.
2. The main function is to convert the source type QObject to the target type DesType (or subtype) in angle brackets, and
return a pointer to the target type. If the conversion fails, 0 is returned. Or if the source type QObject belongs to the target
type DestType (or its subtype), then a pointer to the target type is returned, otherwise 0 is returned.
3. Conditions for using qobject_cast: The target type DestType must inherit (directly or indirectly) from QObject and
use the Q_OBJECT macro.

An example:

head File:

#ifndef M_H //要使用元对象系统,需在头文件中定义类。
#define M_H
#include<QObject>
#include <iostream>
using namespace std;
class A:public QObject{
    
     Q_OBJECT
public:void fa(){
    
    cout<<"FA"<<endl;}  };
class B:public A{
    
     Q_OBJECT
public:void fb(){
    
    cout<<"FB"<<endl;}  };
class C:public QObject{
    
    Q_OBJECT
public:void fc(){
    
    cout<<"FC"<<endl;}  };
class D:public C{
    
     public: void fd(){
    
    cout<<"FD"<<endl;} };
#endif // M_H

Source File:

#include "m.h"
#include <QMetaMethod>
#include <iostream>
using namespace std;
//qobject_cast 的简单应用(类型判断)
void g(QObject *p){
    
    
if(qobject_cast<A*>(p)) //若 p 是类 A 及其派生类类型
{
    
    cout<<"GA"<<endl;}
if(qobject_cast<B*>(p))//若 p 是类 B 及其派生类类型
{
    
    cout<<"GB"<<endl;}
else //若 p 不是类 B 及其派生类类型
cout<<"XX"<<endl;  }
int main(){
    
      A *pa=new A; B *pb=new B; C *pc=new C; D *pd=new D;
qobject_cast<B*>(pa)->fb(); //输出 FB,转换成功后可调用子类中的函数。
//qobject_cast<D*>(pc); //错误,因为类 D 未使用 Q_OBJECT 宏。
g(pa); //输出 GA、XX。因为 pa 不是 B 及其派生类类型所以会输出 XX。
g(pb); //输出 GA、GB。因为 pb 是 A 的派生类类型,所以首先输出 GA,然后输出 GB。
g(pc); //输出 XX,因为 pc 即不是 A 也不是 B 及其派生类的类型,所以输出 XX。
g(pd); //输出 XX,原因同上。
return 0; }

To be continued! ! !

Guess you like

Origin blog.csdn.net/m0_43458204/article/details/131765616