[QT] Notas de estudio del sistema meta-objeto (1)

01. Sistema de metaobjetos

El sistema de metaobjetos es una extensión de Qt al C ++ original. Se introduce principalmente para implementar el mecanismo de señal y ranura. El mecanismo de señal y ranura es la característica principal de Qt.

Las funciones proporcionadas por el sistema de metaobjetos de Qt son:
1. Mecanismo de señal y ranura para la comunicación entre objetos
2. Información de tipo de tiempo de ejecución y sistema de atributos dinámicos, etc.

Para utilizar la funcionalidad del sistema de metaobjetos, se deben cumplir tres requisitos previos.

  1. Esta clase debe heredar de la clase QObject.
  2. Se debe agregar una macro en el área privada de la declaración de clase Q_OBJECT, que se usa para habilitar las propiedades del metaobjeto, y luego puede usar funciones como propiedades dinámicas, señales y ranuras.
  3. El compilador de metaobjetos (moc) proporciona a cada subclase de QObject el código necesario para implementar las características del metaobjeto.
1.1 Principios operativos del metaobjeto
  • Debido a que el sistema de metaobjetos es una extensión de C++, los programas Qt con el sistema de metaobjetos habilitado no se pueden compilar directamente usando compiladores tradicionales. Antes de compilar el programa Qt, es necesario eliminar la sintaxis extendida. Esta función es la función moc. Cosas para hacer.
  • El nombre completo de moc es: Meta-Object Compiler. Es una herramienta (similar a qmake). Esta herramienta lee y analiza archivos fuente de C++. Si se encuentran una o más declaraciones de clase que contienen macros, entonces otro archivo fuente de C++ que contiene la Q_OBJECTmacro Q_OBJECT. Se generará el código de implementación (el archivo fuente generalmente se llama: moc_*.cpp). Este nuevo archivo fuente se incluirá en el archivo fuente de la clase o se compilará y descifrará en la implementación de la clase (generalmente este enfoque es usado). Nota: El nuevo archivo no "reemplazará" al archivo antiguo, pero los mismos archivos fuente se compilarán juntos.

Otros conceptos:

1. Código de metaobjeto: se refiere al código del archivo fuente generado por la herramienta moc, que contiene el Q_OBJECTcódigo de implementación de macro
. 2. La ruta de la herramienta moc generalmente se encuentra en la ruta de instalación, como: C: \ app \Qt5.8.0MinGw\5.8 \mingw53_32\bin

1.2, Q_OBJECT Hiroshi

Primero mire una imagen, siempre que contenga Q_OBJECTmacros, y luego F2, puede ingresar.
Insertar descripción de la imagen aquí
Se puede ver que Q_OBJECTla macro agrega algunos miembros a la clase declarada, y los cuadros rojos marcan los miembros de la función virtual (tenga en cuenta que estas funciones virtuales no están definidas). Según la sintaxis de C ++, las funciones virtuales deben definirse o declararse como virtuales puras. funciones, herramienta moc Uno de sus trabajos es generar las definiciones de los miembros anteriores y también generar algún otro código necesario.

1.3 Qt Creator inicia el sistema de metaobjetos

Dado que la herramienta moc se utiliza a través de Qt Creator, se debe garantizar que moc pueda descubrir y procesar Q_OBJECTclases que contienen macros en el proyecto, por lo que se deben seguir las siguientes reglas:
1. La definición de clases que contienen macros derivadas de QObject Q_OBJECTdebe ser en el archivo de encabezado 2.
Asegúrese prode que todos los archivos fuente (variable SOURCES) y archivos de encabezado (variable HEADERS) en el proyecto estén enumerados en el archivo 3. Se
deben usar instrucciones lógicas como () en el archivo de encabezado #ifndef、#define、#endifpara evitar que el encabezado archivos se incluyan varias veces.
4. La clase QObject debe ser la primera clase en la lista de clases base.

Se puede ver en las reglas anteriores que al escribir código usando Qt Creator, la clase debe definirse en el archivo de encabezado y la
definición de la función miembro debe ubicarse en el archivo fuente (esto puede evitar errores de redefinición causados ​​​​por incluir el archivo de encabezado varias veces) Aunque el
programa está escrito de esta manera Es engorroso, pero es una buena manera de organizar su código.

Nota: Si se define y construye una clase derivada de la clase QObject (la compilación no se agrega O_OBJECT, pero se agrega después de que se completa la compilación), el comando qmake debe ejecutarse nuevamente en este momento; de lo contrario, moc no puede generar código.

Aquí están las palabras originales del libro: Ejecute
Insertar descripción de la imagen aquí
el programa anterior y podrá encontrar un archivo fuente moc_m.cpp en el directorio de depuración. Este archivo fuente se
genera usando la herramienta moc. El código en el archivo fuente es el código de metaobjeto. Los lectores pueden verlo, su código. Si no hay ningún
archivo moc_m.cpp en este directorio, significa que la herramienta moc no pudo iniciarse normalmente. En este caso, debe ejecutar el comando qmake en Qt Creator y
luego compilar el programa.

1.4 Metaobjeto de inicio de línea de comando (no se usa comúnmente)
  1. Debe utilizar la herramienta moc en la línea de comando e incluir el archivo cpp generado por la herramienta moc en el archivo fuente.
  2. En este momento, no es necesario ubicar la clase que contiene Q_OBJECT en el archivo de encabezado. Suponiendo que esté ubicada en el archivo m.cpp, el contenido es:
#include<QObject>
class A : public QObject
{
    
      
	Q_OBJECT
public:
  A(){
    
    }  
};

int main(int argc, char *argv[]) {
    
    
 	A ma; return 0; 
}
  1. Abra la herramienta de línea de comando Qt 5.8 para escritorio (MinGW 5.3.0 32 bits) e ingrese el siguiente comando:
moc d:\qt\m.cpp -o d:\qt\mm.cpp

El comando anterior generará el archivo mm.cpp basado en m.cpp. El código en el archivo mm.cpp es el código del metaobjeto. Aquí,
m.cpp y mm.cpp están ubicados en la carpeta d:\qt .

  1. Luego abra m.cpp nuevamente y use #include para incluir mm.cpp, de la siguiente manera:
#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. Luego use qmake para generar el archivo del proyecto y el archivo makefile, y luego use el comando mingw32-make.

En términos generales, los desarrolladores de QT rara vez usan el método de línea de comando, porque Qt Creator ya lo ha hecho por nosotros. Lo vi mencionado en el libro y lo registré aquí.

02. Mecanismo de reflexión

Modo de reflexión (modo de reflexión / mecanismo de reflexión): se refiere a un mecanismo que puede obtener todo tipo de información, atributos, funciones miembro y otra información de cualquier objeto de clase en tiempo de ejecución.

Sistema de metaobjetos y mecanismo de reflexión:
1. Una de las funciones proporcionadas por el sistema de metaobjetos es proporcionar información de tipo de tiempo de ejecución y valores actuales de
los miembros En otras palabras, durante la fase de ejecución, el programa puede obtener
el nombre de la clase a la que pertenece el objeto de clase derivada de QObject , el nombre de la clase principal, las funciones miembro del objeto, los tipos de enumeración, los miembros de datos y otra información, de hecho, este es
el mecanismo de reflexión.

2. Debido a que el sistema de metaobjetos de Qt debe heredar de QObject y se puede ver el papel principal del mecanismo de reflexión, el
sistema de metaobjetos de Qt proporciona principalmente al programa información sobre los objetos de clase QObject y sus objetos de clase derivados, es decir , no
es un objeto de clase derivado de QObject, no puede usar el sistema de metaobjetos de Qt para obtener esta información.

La explicación específica de metaobjeto: se refiere a un objeto utilizado para describir la estructura de otro objeto.

Traducido concretamente a un programa de la siguiente manera:

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

class A : public B
{
    
    
	B mb;
};

Suponiendo que mb se usa para describir objetos creados por la clase A, entonces mb es un metaobjeto.

2.1 Qt implementa el mecanismo de reflexión
  1. Qt utiliza una serie de clases para implementar el mecanismo de reflexión. Estas clases describen cada lado del objeto. La clase QMetaObjectdescribe toda la metainformación de QObject y sus objetos de clase derivados. Esta clase es la clase central del sistema de metaobjetos de Qt. A través de Las funciones miembro de esta clase se puede obtener toda la metainformación de QObject y sus objetos de clase derivados, por lo que se puede decir que los QMetaObjectobjetos de la clase son metaobjetos en Qt. Nota: Para llamar QMetaObjecta funciones miembro en una clase, debe utilizar QMetaObjectun objeto del tipo.
  2. Describa los miembros del objeto: un objeto contiene miembros de datos, miembros de funciones, constructores, miembros de enumeración, etc. En Qt, estos miembros se describen utilizando diferentes clases. Por ejemplo, los miembros de una función se describen mediante clases QMetaMethody atributos . QMetaPropertyFinalmente, use la clase QMetaObject para describir el objeto de clase completo, por ejemplo, para obtener el nombre de la variable de la función miembro:
QMetaMethod qm = metaObject->method(1);  // 获取索引为1的成员函数
qDebug()<< qm.name() << "\n";  // 输出该成员函数的名称

Requisitos previos para utilizar el mecanismo de reflexión de Qt:

  1. Debe heredarse de la clase QObject y agregar la macro Q_OBJECT a la clase.
  2. QObject::Q_INVOKABLERegistro de funciones miembro: si desea que se reflejen las funciones miembro ordinarias, debe agregar macros antes de la declaración de la función .
  3. Registre variables miembro: si desea que se reflejen las variables miembro, debe utilizar Q_PROPERTYmacros.

De hecho QObject::Q_INVOKABLE, Q_PROPERTYtengo un conocimiento profundo de estas dos macros en QQuick en Qt5. Si QML quiere utilizar las propiedades y métodos de C ++, estas macros deben marcarse.

Breve descripción del principio de implementación del mecanismo de reflexión Qt:

  1. Q_OBJECTDespués de la expansión macro, hay una función miembro virtual meteObject(), que devuelve un QMetaObjectpuntero al tipo y su prototipo es:
virtual const QMetaObject* metaObject() const;

Debido a que todas las clases que habilitan el sistema de metaobjetos contienen Q_OBJECTmacros, todas estas clases contienen funciones miembro virtuales. Al llamar a las funciones miembro en la clase metaObject()a través del puntero devuelto por la función , puede consultar diversa información sobre QObject y sus objetos de clase derivados. .QMetaObject

  1. El moc de Qt completará el siguiente trabajo:
    1. Q_OBJECTGenerar código de implementación para las funciones miembro declaradas después de la expansión de la macro; 2. Identificar
    palabras clave especiales y macros en Qt, como identificar Q_PROPRTY,,, etc.Q_INVOKABLEslotsignals
2.2 Mecanismo de reflexión para obtener información sobre funciones miembro en una clase
2.1.1, clase QMetaMethon

Función: se utiliza para describir las funciones miembro de un objeto. Puede utilizar las funciones miembro de esta clase para obtener información sobre las funciones miembro del objeto.

Enumere algunos miembros de uso común:

// 此枚举用于描述函数的类型,即:普通成员函数、信号、槽、构造函数
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 Clase QMetaObject

Función: Se utiliza para proporcionar información de metaobjeto sobre la clase.

Enumere algunos miembros de uso común:

/* 返回名为 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;

El siguiente es un ejemplo del libro solo como referencia.

#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 Mecanismo de reflexión para obtener información relacionada con la clase.
  1. Las funciones miembro de la clase QMetaObject para obtener información relacionada con la clase son:
/*获取类的名称,注意,若某个 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. Las funciones miembro de la clase QObject para obtener información relacionada con la clase son:
// 若该类是className指定的类的子类则返回true,否则返回false。类被认为继承自身
bool inherits(const char* className) const;

He aquí un ejemplo:

Archivo de cabeza:

#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

Archivo fuente:

#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. La función qobject_cast utiliza la siguiente sintaxis:
DestType* qobject_cast<DestType*>(QObject* p);

1. Esta función es similar a dinámica_cast en C ++, pero su velocidad de ejecución es más rápida que dinámica_cast y no requiere el
soporte de RTTI de C ++. Sin embargo, qobject_cast solo se aplica a QObject y sus clases derivadas.
2. La función principal es convertir el tipo de origen QObject al tipo de destino DesType (o subtipo) entre paréntesis angulares y
devolver un puntero al tipo de destino. Si la conversión falla, se devuelve 0. O si el tipo de origen QObject pertenece al
tipo de destino DestType (o su subtipo), entonces se devuelve un puntero al tipo de destino; de lo contrario, se devuelve 0.
3. Condiciones para usar qobject_cast: el tipo de destino DestType debe heredar (directa o indirectamente) de QObject y
usar la macro Q_OBJECT.

Un ejemplo:

Archivo de cabeza:

#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

Archivo fuente:

#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; }

¡Continuará! ! !

Supongo que te gusta

Origin blog.csdn.net/m0_43458204/article/details/131765616
Recomendado
Clasificación