什么是 Qt 的元对象系统?
一、什么是 Qt 的元对象系统?
Qt 的 元对象系统(Meta-Object System) 是 Qt 在标准 C++ 之上扩展的一层反射机制
简单说,就是让 C++ 类能在运行时知道自己的结构信息(类名、属性、信号、槽等)
这在纯 C++ 中是做不到的(因为 C++ 没有内置反射机制)
Qt 用一个独立的工具 moc
(Meta Object Compiler) 来实现这个扩展
它会在编译阶段扫描含有 Q_OBJECT
宏的类,并生成额外的 .moc
文件,保存类的“自我描述信息”
二、举例说明
class TPerson : public QObject
{Q_OBJECTQ_CLASSINFO("author","Wang")Q_CLASSINFO("company","UPC")Q_CLASSINFO("version","2.0.0")Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged)Q_PROPERTY(QString name MEMBER m_name)Q_PROPERTY(int score MEMBER m_score)
public:explicit TPerson(QString aName, QObject *parent = nullptr);~TPerson();int age();void setAge(int value);void incAge();
private:int m_age = 10;int m_score = 79;
};
从这段代码开始,我们能看到所有元对象系统的核心要素:
宏 | 功能 |
---|---|
Q_OBJECT | 告诉 moc :这个类需要生成元对象数据 |
Q_CLASSINFO | 添加类的额外描述信息 |
Q_PROPERTY | 注册可反射的属性 |
signals/slots (如果有) | 注册信号和槽函数 |
三、moc
工具做了什么?
当编译器看到 Q_OBJECT
时,moc
会自动为 TPerson
生成一个隐藏文件,比如:
moc_tperson.cpp
里面会生成一个 QMetaObject 静态实例:
const QMetaObject TPerson::staticMetaObject = {{ &QObject::staticMetaObject,qt_meta_stringdata_TPerson.data,qt_meta_data_TPerson,nullptr, nullptr, nullptr }
};
它保存了:
- 类名
"TPerson"
- 所有 Q_PROPERTY 信息(属性名、类型、getter/setter、NOTIFY 信号等)
- 所有信号和槽的函数签名
- 所有 Q_CLASSINFO 元数据
- 父类的元对象指针(实现继承链)
四、运行时怎么用这些信息?
在程序运行时,每个 QObject 派生类对象都携带一个指向这个元对象的指针:
boy->metaObject() // 就能获取 TPerson 的 QMetaObject
然后你就能在运行时做以下事:
获取类信息
qDebug() << boy->metaObject()->className();
// 输出: "TPerson"
获取 Q_CLASSINFO 信息
for (int i=0; i<meta->classInfoCount(); ++i) {QMetaClassInfo info = meta->classInfo(i);qDebug() << info.name() << ":" << info.value();
}
输出:
author : Wang
company : UPC
version : 2.0.0
获取属性并动态访问
int count = meta->propertyCount();
for (int i = 0; i < count; ++i) {QMetaProperty prop = meta->property(i);qDebug() << "Property:" << prop.name();
}
可以通过属性名来动态设置或读取:
boy->setProperty("score", 95);
qDebug() << boy->property("score"); // 输出 95
即使类中没有 setProperty()
,也能通过 QObject
的通用接口做到
五、元对象系统让信号槽成为可能
信号槽机制其实就是元对象系统的一个直接应用
当你写:
connect(boy, &TPerson::ageChanged, this, &Widget::onAgeChanged);
moc
会把所有信号、槽函数都注册进 QMetaObject
Qt 的运行时系统会用这些信息在对象间动态建立连接,不需要编译时就知道函数地址
这就是为什么信号与槽能“用字符串匹配”工作,甚至跨线程、跨模块通信
六、动态属性(Dynamic Property)
如果你调用:
boy->setProperty("sex", "Boy");
而 "sex"
并没有在 Q_PROPERTY 中声明,
Qt 会自动为对象创建一个 动态属性(存在内部哈希表中)
可随时访问:
qDebug() << boy->property("sex"); // 输出 "Boy"
但动态属性不会出现在 QMetaObject::propertyCount()
结果里