原文https://blog.csdn.net/wanywhn/article/details/80293581
简说内省
所谓内省是指面向对象语言的一种在运行期间查询对象信息的能力, 比如如果该语具有运行期间检查对象型别的能力,那么我们称它是型别内省(type intropection)的,型别内省可以用来实施多态。
C++的内省比较有限,它仅支持上面所说的型别内省, C++的型别内省是通过运行时类型识别(RTTI)(Run-Time Type Information)中的typeid 以及dynamic_case关键字来实现的,举例说明。
// rabbit 派生于 Animal, jump为虚函数class Animal{virtual void jump();};class rabbit: public Animal{virtual void jump();};if ( rabbit *p == dynamic_case<Animal*>(obj)){p->jump();}//我们还可以通过typeid萃取到对象的型别信息,比如对象的名称std::cout << typeid(obj).name() << std::endl
Qt内省
Qt拓展了C++的内省机制,(实际上,它并没有采用C++的RTTI),而是提供了更为强大的元对象(meta object)机制,来实现内省。接下来,就让我们看看,Qt是如何扩展c++内省机制的。
要深刻理解Qt的内省机制,首先理解QObject,QObject类是整个Qt对象模型的心脏,Qt对象模型最为核心的功能是提供一种无缝的对象通讯机制,即就是我们所熟知的信号和槽。QObject主要有三大职责: 内存管理、内省(intropection)与事件处理。本文将集中在在内省的讨论。以下代码介绍了QObject类提供的内省方法:
//每个对象可以通过QObject::setObjectName()和QObject::objectName()设置、取得类的实例的名字rabbit obj;obj.setObjectName("instanceName");QString name1 = obj.objectName(); // return instanceName//每个对象还可以通过它的元对象className方法得到类的名字QString name2 = obj.metaObject()->className(); // return rabbit//每个对象可以通过QObject::inherits方法来查询是否对前对象类派生于量一个类QLabel obj;bool isherited = obj.inherits("QObject"); // returns trueisherited = obj.inherits("QWideget"); // returns true
再来一下QObject::inherits方法的底层实现:
inline bool inherits(const char *classname) const{ return const_cast<QObject *>(this)->qt_metacast(classname) != 0; }
原来,QObject::inherits是通过qt_metacast()这个虚函数实现的, 事实上每个QObject的派生类都必须实现metaObject()以及其他qt_metacall()方法,从而满足自省方法className, inherits等方法的调用(当然还有其他用途)。而所有有关派生从QObject的子类中的内省方法无须有用户实现,用户只要在类中声明宏Q_OBJECT即可,Qt的元对象编译器(moc)负责实现派生QObject的子类中的内省方法。
/* tmake ignore Q_OBJECT */#define Q_OBJECT \public: \Q_OBJECT_CHECK \static const QMetaObject staticMetaObject; \Q_OBJECT_GETSTATICMETAOBJECT \virtual const QMetaObject *metaObject() const; \virtual void *qt_metacast(const char *); \QT_TR_FUNCTIONS \virtual int qt_metacall(QMetaObject::Call, int, void **); \private: \Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; \Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
此外,所有的Qt widgets类均继承自QObject, QObject所提供的isWidgetType自省方法可以很方便让QObject子对象查询自己是否是Wideget, 而且它会比 qobject_cast<QWidget *>(obj) 或者obj->inherits快很多。原因qobject_cast()和inherits()都是借助元对象系统来实现其功能的(详见红色注解),isWidgetType()是QObject本身的标志位得以实现。
QMetaObject自省方法
更多自省方法定义在QMetaObject,以下是QMetaObject声明的源代码:
struct Q_CORE_EXPORT QMetaObject{const char *className() const;const QMetaObject *superClass() const;QObject *cast(QObject *obj) const;const QObject *cast(const QObject *obj) const;#ifndef QT_NO_TRANSLATION// ### Qt 4: Merge overloadsQString tr(const char *s, const char *c) const;QString trUtf8(const char *s, const char *c) const;QString tr(const char *s, const char *c, int n) const;QString trUtf8(const char *s, const char *c, int n) const;#endif // QT_NO_TRANSLATIONint methodOffset() const;int enumeratorOffset() const;int propertyOffset() const;int classInfoOffset() const;int constructorCount() const;int methodCount() const;int enumeratorCount() const;int propertyCount() const;int classInfoCount() const;int indexOfConstructor(const char *constructor) const;int indexOfMethod(const char *method) const;int indexOfSignal(const char *signal) const;int indexOfSlot(const char *slot) const;int indexOfEnumerator(const char *name) const;int indexOfProperty(const char *name) const;int indexOfClassInfo(const char *name) const;QMetaMethod constructor(int index) const;QMetaMethod method(int index) const;QMetaEnum enumerator(int index) const;QMetaProperty property(int index) const;QMetaClassInfo classInfo(int index) const;QMetaProperty userProperty() const;static bool checkConnectArgs(const char *signal, const char *method);static QByteArray normalizedSignature(const char *method);static QByteArray normalizedType(const char *type);// internal index-based connectstatic bool connect(const QObject *sender, int signal_index,const QObject *receiver, int method_index,int type = 0, int *types = 0);// internal index-based disconnectstatic bool disconnect(const QObject *sender, int signal_index,const QObject *receiver, int method_index);static bool disconnectOne(const QObject *sender, int signal_index,const QObject *receiver, int method_index);// internal slot-name based connectstatic void connectSlotsByName(QObject *o);// internal index-based signal activationstatic void activate(QObject *sender, int signal_index, void **argv); //obsoletestatic void activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv); //obsoletestatic void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);static void activate(QObject *sender, const QMetaObject *, int from_local_signal_index, int to_local_signal_index, void **argv); //obsolete// internal guarded pointersstatic void addGuard(QObject **ptr);static void removeGuard(QObject **ptr);static void changeGuard(QObject **ptr, QObject *o);static bool invokeMethod(QObject *obj, const char *member,Qt::ConnectionType,QGenericReturnArgument ret,QGenericArgument val0 = QGenericArgument(0),QGenericArgument val1 = QGenericArgument(),QGenericArgument val2 = QGenericArgument(),QGenericArgument val3 = QGenericArgument(),QGenericArgument val4 = QGenericArgument(),QGenericArgument val5 = QGenericArgument(),QGenericArgument val6 = QGenericArgument(),QGenericArgument val7 = QGenericArgument(),QGenericArgument val8 = QGenericArgument(),QGenericArgument val9 = QGenericArgument());static inline bool invokeMethod(QObject *obj, const char *member,QGenericReturnArgument ret,QGenericArgument val0 = QGenericArgument(0),QGenericArgument val1 = QGenericArgument(),QGenericArgument val2 = QGenericArgument(),QGenericArgument val3 = QGenericArgument(),QGenericArgument val4 = QGenericArgument(),QGenericArgument val5 = QGenericArgument(),QGenericArgument val6 = QGenericArgument(),QGenericArgument val7 = QGenericArgument(),QGenericArgument val8 = QGenericArgument(),QGenericArgument val9 = QGenericArgument()){return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3,val4, val5, val6, val7, val8, val9);}static inline bool invokeMethod(QObject *obj, const char *member,Qt::ConnectionType type,QGenericArgument val0 = QGenericArgument(0),QGenericArgument val1 = QGenericArgument(),QGenericArgument val2 = QGenericArgument(),QGenericArgument val3 = QGenericArgument(),QGenericArgument val4 = QGenericArgument(),QGenericArgument val5 = QGenericArgument(),QGenericArgument val6 = QGenericArgument(),QGenericArgument val7 = QGenericArgument(),QGenericArgument val8 = QGenericArgument(),QGenericArgument val9 = QGenericArgument()){return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2,val3, val4, val5, val6, val7, val8, val9);}static inline bool invokeMethod(QObject *obj, const char *member,QGenericArgument val0 = QGenericArgument(0),QGenericArgument val1 = QGenericArgument(),QGenericArgument val2 = QGenericArgument(),QGenericArgument val3 = QGenericArgument(),QGenericArgument val4 = QGenericArgument(),QGenericArgument val5 = QGenericArgument(),QGenericArgument val6 = QGenericArgument(),QGenericArgument val7 = QGenericArgument(),QGenericArgument val8 = QGenericArgument(),QGenericArgument val9 = QGenericArgument()){return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0,val1, val2, val3, val4, val5, val6, val7, val8, val9);}QObject *newInstance(QGenericArgument val0 = QGenericArgument(0),QGenericArgument val1 = QGenericArgument(),QGenericArgument val2 = QGenericArgument(),QGenericArgument val3 = QGenericArgument(),QGenericArgument val4 = QGenericArgument(),QGenericArgument val5 = QGenericArgument(),QGenericArgument val6 = QGenericArgument(),QGenericArgument val7 = QGenericArgument(),QGenericArgument val8 = QGenericArgument(),QGenericArgument val9 = QGenericArgument()) const;enum Call {InvokeMetaMethod,ReadProperty,WriteProperty,ResetProperty,QueryPropertyDesignable,QueryPropertyScriptable,QueryPropertyStored,QueryPropertyEditable,QueryPropertyUser,CreateInstance};int static_metacall(Call, int, void **) const;static int metacall(QObject *, Call, int, void **);#ifdef QT3_SUPPORTQT3_SUPPORT const char *superClassName() const;#endifstruct { // private dataconst QMetaObject *superdata;const char *stringdata;const uint *data;const void *extradata;} d;};
红色注解
注:红色下滑线部分,我不认同。我们可以来看看qobject_cast
(obj)的实现源码:
template <> inline QWidget *qobject_cast<QWidget*>(QObject *o){if (!o || !o->isWidgetType())return 0;return static_cast<QWidget*>(o);}template <> inline const QWidget *qobject_cast<const QWidget*>(const QObject *o){if (!o || !o->isWidgetType())return 0;return static_cast<const QWidget*>(o);}
通过以上源码可以看出,qobject_cast<QWidget *>(obj)的实现实际上也是通过isWidgetType()来进行判断的。我们来继续看看isWidgetType()的实现,代码如下:
inline bool isWidgetType() const { return d_ptr->isWidget; }
而d_ptr是定义为:
QScopedPointer<QObjectData> d_ptr;
QObjectData是QObject的数据结构体,其数据为:
QObjectData {public:virtual ~QObjectData() = 0;QObject *q_ptr;QObject *parent;QObjectList children;uint isWidget : 1;uint pendTimer : 1;uint blockSig : 1;uint wasDeleted : 1;uint ownObjectName : 1;uint sendChildEvents : 1;uint receiveChildEvents : 1;uint inEventHandler : 1; //only used if QT_JAMBI_BUILDuint inThreadChangeEvent : 1;uint hasGuards : 1; //true iff there is one or more QPointer attached to this objectuint unused : 22;int postedEvents;QMetaObject *metaObject; // assert dynamic};
从上可以看出,isWidgetType() 的实现的是从内部本身标识为识别,而qobject_cast<QWidget *>(obj)也是用isWidgetType(),何来快与不快之说。 而至于obj->inherits,其实现代码如下:
inline bool inherits(const char *classname) const{ return const_cast<QObject *>(this)->qt_metacast(classname) != 0; }
总结
- Qt是通过
QObject、QMetaObject类实现其内省机制, QObject暴露给用户的共有自省方法有objectName(),inherits(),isWidgetType()等- 大多数自省方法是
QObject派发给QMetaObject实现 (e.g.QMetaObject::className),元对象模型编译器moc负责自省方法的实现
4、更多自省方法定义在QMetaObject,是为了信号槽通讯、事件派发等机制
