原文https://blog.csdn.net/wanywhn/article/details/80293581

简说内省

所谓内省是指面向对象语言的一种在运行期间查询对象信息的能力, 比如如果该语具有运行期间检查对象型别的能力,那么我们称它是型别内省(type intropection)的,型别内省可以用来实施多态。

C++的内省比较有限,它仅支持上面所说的型别内省, C++的型别内省是通过运行时类型识别(RTTI)(Run-Time Type Information)中的typeid 以及dynamic_case关键字来实现的,举例说明。

  1. // rabbit 派生于 Animal, jump为虚函数
  2. class Animal{
  3. virtual void jump();
  4. };
  5. class rabbit: public Animal{
  6. virtual void jump();
  7. };
  8. if ( rabbit *p == dynamic_case<Animal*>(obj))
  9. {
  10. p->jump();
  11. }
  12. //我们还可以通过typeid萃取到对象的型别信息,比如对象的名称
  13. std::cout << typeid(obj).name() << std::endl

Qt内省

Qt拓展了C++的内省机制,(实际上,它并没有采用C++的RTTI),而是提供了更为强大的元对象(meta object)机制,来实现内省。接下来,就让我们看看,Qt是如何扩展c++内省机制的。
要深刻理解Qt的内省机制,首先理解QObject,QObject类是整个Qt对象模型的心脏,Qt对象模型最为核心的功能是提供一种无缝的对象通讯机制,即就是我们所熟知的信号和槽。QObject主要有三大职责: 内存管理、内省(intropection)与事件处理。本文将集中在在内省的讨论。以下代码介绍了QObject类提供的内省方法:

  1. //每个对象可以通过QObject::setObjectName()和QObject::objectName()设置、取得类的实例的名字
  2. rabbit obj;
  3. obj.setObjectName("instanceName");
  4. QString name1 = obj.objectName(); // return instanceName
  5. //每个对象还可以通过它的元对象className方法得到类的名字
  6. QString name2 = obj.metaObject()->className(); // return rabbit
  7. //每个对象可以通过QObject::inherits方法来查询是否对前对象类派生于量一个类
  8. QLabel obj;
  9. bool isherited = obj.inherits("QObject"); // returns true
  10. isherited = obj.inherits("QWideget"); // returns true

再来一下QObject::inherits方法的底层实现:

  1. inline bool inherits(const char *classname) const
  2. { 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的子类中的内省方法。

  1. /* tmake ignore Q_OBJECT */
  2. #define Q_OBJECT \
  3. public: \
  4. Q_OBJECT_CHECK \
  5. static const QMetaObject staticMetaObject; \
  6. Q_OBJECT_GETSTATICMETAOBJECT \
  7. virtual const QMetaObject *metaObject() const; \
  8. virtual void *qt_metacast(const char *); \
  9. QT_TR_FUNCTIONS \
  10. virtual int qt_metacall(QMetaObject::Call, int, void **); \
  11. private: \
  12. Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; \
  13. 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声明的源代码:

  1. struct Q_CORE_EXPORT QMetaObject
  2. {
  3. const char *className() const;
  4. const QMetaObject *superClass() const;
  5. QObject *cast(QObject *obj) const;
  6. const QObject *cast(const QObject *obj) const;
  7. #ifndef QT_NO_TRANSLATION
  8. // ### Qt 4: Merge overloads
  9. QString tr(const char *s, const char *c) const;
  10. QString trUtf8(const char *s, const char *c) const;
  11. QString tr(const char *s, const char *c, int n) const;
  12. QString trUtf8(const char *s, const char *c, int n) const;
  13. #endif // QT_NO_TRANSLATION
  14. int methodOffset() const;
  15. int enumeratorOffset() const;
  16. int propertyOffset() const;
  17. int classInfoOffset() const;
  18. int constructorCount() const;
  19. int methodCount() const;
  20. int enumeratorCount() const;
  21. int propertyCount() const;
  22. int classInfoCount() const;
  23. int indexOfConstructor(const char *constructor) const;
  24. int indexOfMethod(const char *method) const;
  25. int indexOfSignal(const char *signal) const;
  26. int indexOfSlot(const char *slot) const;
  27. int indexOfEnumerator(const char *name) const;
  28. int indexOfProperty(const char *name) const;
  29. int indexOfClassInfo(const char *name) const;
  30. QMetaMethod constructor(int index) const;
  31. QMetaMethod method(int index) const;
  32. QMetaEnum enumerator(int index) const;
  33. QMetaProperty property(int index) const;
  34. QMetaClassInfo classInfo(int index) const;
  35. QMetaProperty userProperty() const;
  36. static bool checkConnectArgs(const char *signal, const char *method);
  37. static QByteArray normalizedSignature(const char *method);
  38. static QByteArray normalizedType(const char *type);
  39. // internal index-based connect
  40. static bool connect(const QObject *sender, int signal_index,
  41. const QObject *receiver, int method_index,
  42. int type = 0, int *types = 0);
  43. // internal index-based disconnect
  44. static bool disconnect(const QObject *sender, int signal_index,
  45. const QObject *receiver, int method_index);
  46. static bool disconnectOne(const QObject *sender, int signal_index,
  47. const QObject *receiver, int method_index);
  48. // internal slot-name based connect
  49. static void connectSlotsByName(QObject *o);
  50. // internal index-based signal activation
  51. static void activate(QObject *sender, int signal_index, void **argv); //obsolete
  52. static void activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv); //obsolete
  53. static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);
  54. static void activate(QObject *sender, const QMetaObject *, int from_local_signal_index, int to_local_signal_index, void **argv); //obsolete
  55. // internal guarded pointers
  56. static void addGuard(QObject **ptr);
  57. static void removeGuard(QObject **ptr);
  58. static void changeGuard(QObject **ptr, QObject *o);
  59. static bool invokeMethod(QObject *obj, const char *member,
  60. Qt::ConnectionType,
  61. QGenericReturnArgument ret,
  62. QGenericArgument val0 = QGenericArgument(0),
  63. QGenericArgument val1 = QGenericArgument(),
  64. QGenericArgument val2 = QGenericArgument(),
  65. QGenericArgument val3 = QGenericArgument(),
  66. QGenericArgument val4 = QGenericArgument(),
  67. QGenericArgument val5 = QGenericArgument(),
  68. QGenericArgument val6 = QGenericArgument(),
  69. QGenericArgument val7 = QGenericArgument(),
  70. QGenericArgument val8 = QGenericArgument(),
  71. QGenericArgument val9 = QGenericArgument());
  72. static inline bool invokeMethod(QObject *obj, const char *member,
  73. QGenericReturnArgument ret,
  74. QGenericArgument val0 = QGenericArgument(0),
  75. QGenericArgument val1 = QGenericArgument(),
  76. QGenericArgument val2 = QGenericArgument(),
  77. QGenericArgument val3 = QGenericArgument(),
  78. QGenericArgument val4 = QGenericArgument(),
  79. QGenericArgument val5 = QGenericArgument(),
  80. QGenericArgument val6 = QGenericArgument(),
  81. QGenericArgument val7 = QGenericArgument(),
  82. QGenericArgument val8 = QGenericArgument(),
  83. QGenericArgument val9 = QGenericArgument())
  84. {
  85. return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3,
  86. val4, val5, val6, val7, val8, val9);
  87. }
  88. static inline bool invokeMethod(QObject *obj, const char *member,
  89. Qt::ConnectionType type,
  90. QGenericArgument val0 = QGenericArgument(0),
  91. QGenericArgument val1 = QGenericArgument(),
  92. QGenericArgument val2 = QGenericArgument(),
  93. QGenericArgument val3 = QGenericArgument(),
  94. QGenericArgument val4 = QGenericArgument(),
  95. QGenericArgument val5 = QGenericArgument(),
  96. QGenericArgument val6 = QGenericArgument(),
  97. QGenericArgument val7 = QGenericArgument(),
  98. QGenericArgument val8 = QGenericArgument(),
  99. QGenericArgument val9 = QGenericArgument())
  100. {
  101. return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2,
  102. val3, val4, val5, val6, val7, val8, val9);
  103. }
  104. static inline bool invokeMethod(QObject *obj, const char *member,
  105. QGenericArgument val0 = QGenericArgument(0),
  106. QGenericArgument val1 = QGenericArgument(),
  107. QGenericArgument val2 = QGenericArgument(),
  108. QGenericArgument val3 = QGenericArgument(),
  109. QGenericArgument val4 = QGenericArgument(),
  110. QGenericArgument val5 = QGenericArgument(),
  111. QGenericArgument val6 = QGenericArgument(),
  112. QGenericArgument val7 = QGenericArgument(),
  113. QGenericArgument val8 = QGenericArgument(),
  114. QGenericArgument val9 = QGenericArgument())
  115. {
  116. return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0,
  117. val1, val2, val3, val4, val5, val6, val7, val8, val9);
  118. }
  119. QObject *newInstance(QGenericArgument val0 = QGenericArgument(0),
  120. QGenericArgument val1 = QGenericArgument(),
  121. QGenericArgument val2 = QGenericArgument(),
  122. QGenericArgument val3 = QGenericArgument(),
  123. QGenericArgument val4 = QGenericArgument(),
  124. QGenericArgument val5 = QGenericArgument(),
  125. QGenericArgument val6 = QGenericArgument(),
  126. QGenericArgument val7 = QGenericArgument(),
  127. QGenericArgument val8 = QGenericArgument(),
  128. QGenericArgument val9 = QGenericArgument()) const;
  129. enum Call {
  130. InvokeMetaMethod,
  131. ReadProperty,
  132. WriteProperty,
  133. ResetProperty,
  134. QueryPropertyDesignable,
  135. QueryPropertyScriptable,
  136. QueryPropertyStored,
  137. QueryPropertyEditable,
  138. QueryPropertyUser,
  139. CreateInstance
  140. };
  141. int static_metacall(Call, int, void **) const;
  142. static int metacall(QObject *, Call, int, void **);
  143. #ifdef QT3_SUPPORT
  144. QT3_SUPPORT const char *superClassName() const;
  145. #endif
  146. struct { // private data
  147. const QMetaObject *superdata;
  148. const char *stringdata;
  149. const uint *data;
  150. const void *extradata;
  151. } d;
  152. };

红色注解

:红色下滑线部分,我不认同。我们可以来看看qobject_cast(obj)的实现源码:

  1. template <> inline QWidget *qobject_cast<QWidget*>(QObject *o)
  2. {
  3. if (!o || !o->isWidgetType())
  4. return 0;
  5. return static_cast<QWidget*>(o);
  6. }
  7. template <> inline const QWidget *qobject_cast<const QWidget*>(const QObject *o)
  8. {
  9. if (!o || !o->isWidgetType())
  10. return 0;
  11. return static_cast<const QWidget*>(o);
  12. }

通过以上源码可以看出,qobject_cast<QWidget *>(obj)的实现实际上也是通过isWidgetType()来进行判断的。我们来继续看看isWidgetType()的实现,代码如下:

  1. inline bool isWidgetType() const { return d_ptr->isWidget; }

而d_ptr是定义为:

  1. QScopedPointer<QObjectData> d_ptr;

QObjectData是QObject的数据结构体,其数据为:

  1. QObjectData {
  2. public:
  3. virtual ~QObjectData() = 0;
  4. QObject *q_ptr;
  5. QObject *parent;
  6. QObjectList children;
  7. uint isWidget : 1;
  8. uint pendTimer : 1;
  9. uint blockSig : 1;
  10. uint wasDeleted : 1;
  11. uint ownObjectName : 1;
  12. uint sendChildEvents : 1;
  13. uint receiveChildEvents : 1;
  14. uint inEventHandler : 1; //only used if QT_JAMBI_BUILD
  15. uint inThreadChangeEvent : 1;
  16. uint hasGuards : 1; //true iff there is one or more QPointer attached to this object
  17. uint unused : 22;
  18. int postedEvents;
  19. QMetaObject *metaObject; // assert dynamic
  20. };

从上可以看出,isWidgetType() 的实现的是从内部本身标识为识别,而qobject_cast<QWidget *>(obj)也是用isWidgetType(),何来快与不快之说。 而至于obj->inherits,其实现代码如下:

  1. inline bool inherits(const char *classname) const
  2. { return const_cast<QObject *>(this)->qt_metacast(classname) != 0; }

总结

  1. Qt是通过QObjectQMetaObject类实现其内省机制,
  2. QObject暴露给用户的共有自省方法有objectName(), inherits()isWidgetType()
  3. 大多数自省方法是QObject派发给QMetaObject实现 (e.g. QMetaObject::className),元对象模型编译器moc负责自省方法的实现

4、更多自省方法定义在QMetaObject,是为了信号槽通讯、事件派发等机制