菜鸡一个,随便写写,勿喷。好记性不如烂笔头。

了解qt,第一个绕不过的坎便是qt的元对象系统 QMetaObject。

  1. 1 class Object : public QObject
  2. 2 {
  3. 3 Q_OBJECT
  4. 4 Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged)
  5. 5 Q_PROPERTY(int score READ score WRITE setScore NOTIFY scoreChanged)
  6. 6 Q_CLASSINFO("Author", "Scorpio")
  7. 7 Q_CLASSINFO("Version", "2.0")
  8. 8 Q_CLASSINFO("Department", "wk")
  9. 9 Q_ENUMS(Level)
  10. 10 protected:
  11. 11 static const MyStruct sStruct;
  12. 12 QString m_name;
  13. 13 QString m_level;
  14. 14 int m_age;
  15. 15 int m_score;
  16. 16 public:
  17. 17 enum Level
  18. 18 {
  19. 19 Basic,
  20. 20 Middle,
  21. 21 Advanced
  22. 22 };
  23. 23 public:
  24. 24 Q_INVOKABLE explicit Object(QString name, QObject* parent = 0) :QObject(parent)
  25. 25 {
  26. 26 m_name = name;
  27. 27 setObjectName(m_name);
  28. 28 connect(this, &Object::ageChanged, this, &Object::onAgeChanged);
  29. 29 //connect(this, &Object::ageChanged, this, &Object::onScoreChanged);
  30. 30 connect(this, &Object::scoreChanged, this, &Object::onScoreChanged);
  31. 31 }
  32. 32
  33. 33 int age()const
  34. 34 {
  35. 35 return m_age;
  36. 36 }
  37. 37
  38. 38 Q_INVOKABLE void setAge(const int& age)
  39. 39 {
  40. 40 m_age = age;
  41. 41 emit ageChanged(m_age);
  42. 42 }
  43. 43
  44. 44 Q_INVOKABLE int score()const
  45. 45 {
  46. 46 return m_score;
  47. 47 }
  48. 48
  49. 49 Q_INVOKABLE void setScore(const int& score)
  50. 50 {
  51. 51 m_score = score;
  52. 52 emit scoreChanged(m_score);
  53. 53 }
  54. 54 signals:
  55. 55 void ageChanged(int age);
  56. 56 void scoreChanged(int score);
  57. 57 public slots:
  58. 58
  59. 59 void onAgeChanged(int age)
  60. 60 {
  61. 61 //QObjectPrivate* p = static_cast<QObjectPrivate*> (&(*d_ptr));
  62. 62 //p->receiverList(SIGNAL(ageChanged));
  63. 63 //d_ptr->
  64. 64 qDebug() << "age changed:" << age;
  65. 65 }
  66. 66 void onScoreChanged(int score)
  67. 67 {
  68. 68 qDebug() << "score changed:" << score;
  69. 69 }
  70. 70 };

通常继承qt的类,都会继承于QObject. 在类里添加一句 Q_OBJECT宏。如下所示,是qt信号槽的关键。

  1. 1 #define Q_OBJECT \
  2. 2 public: \
  3. 3 QT_WARNING_PUSH \
  4. 4 Q_OBJECT_NO_OVERRIDE_WARNING \
  5. 5 static const QMetaObject staticMetaObject; \
  6. 6 virtual const QMetaObject *metaObject() const; \
  7. 7 virtual void *qt_metacast(const char *); \
  8. 8 virtual int qt_metacall(QMetaObject::Call, int, void **); \
  9. 9 QT_TR_FUNCTIONS \
  10. 10 private: \
  11. 11 Q_OBJECT_NO_ATTRIBUTES_WARNING \
  12. 12 Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
  13. 13 QT_WARNING_POP \
  14. 14 struct QPrivateSignal {}; \
  15. 15 QT_ANNOTATE_CLASS(qt_qobject, "")

要想编译qt相关类,少不了moc工具。可以理解为qt的预编译工具,moc工具会解析具有Q_OBJECT宏的类,生成对应的moc_xx.cpp文件,该文件会随着项目一起编译。

  1. 1 /****************************************************************************
  2. 2 ** Meta object code from reading C++ file 'Object.h'
  3. 3 **
  4. 4 ** Created by: The Qt Meta Object Compiler version 68 (Qt 6.0.0)
  5. 5 **
  6. 6 ** WARNING! All changes made in this file will be lost!
  7. 7 *****************************************************************************/
  8. 8
  9. 9 #include <memory>
  10. 10 #include "../../../Object.h"
  11. 11 #include <QtCore/qbytearray.h>
  12. 12 #include <QtCore/qmetatype.h>
  13. 13 #if !defined(Q_MOC_OUTPUT_REVISION)
  14. 14 #error "The header file 'Object.h' doesn't include <QObject>."
  15. 15 #elif Q_MOC_OUTPUT_REVISION != 68
  16. 16 #error "This file was generated using the moc from 6.0.0. It"
  17. 17 #error "cannot be used with the include files from this version of Qt."
  18. 18 #error "(The moc has changed too much.)"
  19. 19 #endif
  20. 20
  21. 21 QT_BEGIN_MOC_NAMESPACE
  22. 22 QT_WARNING_PUSH
  23. 23 QT_WARNING_DISABLE_DEPRECATED
  24. 24 struct qt_meta_stringdata_Object_t {
  25. 25 const uint offsetsAndSize[44];
  26. 26 char stringdata0[167];
  27. 27 };
  28. 28 #define QT_MOC_LITERAL(ofs, len) \
  29. 29 uint(offsetof(qt_meta_stringdata_Object_t, stringdata0) + ofs), len
  30. 30 static const qt_meta_stringdata_Object_t qt_meta_stringdata_Object = {
  31. 31 {
  32. 32 QT_MOC_LITERAL(0, 6), // "Object"
  33. 33 QT_MOC_LITERAL(7, 6), // "Author"
  34. 34 QT_MOC_LITERAL(14, 7), // "Scorpio"
  35. 35 QT_MOC_LITERAL(22, 7), // "Version"
  36. 36 QT_MOC_LITERAL(30, 3), // "2.0"
  37. 37 QT_MOC_LITERAL(34, 10), // "Department"
  38. 38 QT_MOC_LITERAL(45, 2), // "wk"
  39. 39 QT_MOC_LITERAL(48, 10), // "ageChanged"
  40. 40 QT_MOC_LITERAL(59, 0), // ""
  41. 41 QT_MOC_LITERAL(60, 3), // "age"
  42. 42 QT_MOC_LITERAL(64, 12), // "scoreChanged"
  43. 43 QT_MOC_LITERAL(77, 5), // "score"
  44. 44 QT_MOC_LITERAL(83, 12), // "onAgeChanged"
  45. 45 QT_MOC_LITERAL(96, 14), // "onScoreChanged"
  46. 46 QT_MOC_LITERAL(111, 6), // "setAge"
  47. 47 QT_MOC_LITERAL(118, 8), // "setScore"
  48. 48 QT_MOC_LITERAL(127, 4), // "name"
  49. 49 QT_MOC_LITERAL(132, 6), // "parent"
  50. 50 QT_MOC_LITERAL(139, 5), // "Level"
  51. 51 QT_MOC_LITERAL(145, 5), // "Basic"
  52. 52 QT_MOC_LITERAL(151, 6), // "Middle"
  53. 53 QT_MOC_LITERAL(158, 8) // "Advanced"
  54. 54
  55. 55 },
  56. 56 "Object\0Author\0Scorpio\0Version\0""2.0\0"
  57. 57 "Department\0wk\0ageChanged\0\0age\0"
  58. 58 "scoreChanged\0score\0onAgeChanged\0"
  59. 59 "onScoreChanged\0setAge\0setScore\0name\0"
  60. 60 "parent\0Level\0Basic\0Middle\0Advanced"
  61. 61 };
  62. 62 #undef QT_MOC_LITERAL
  63. 63
  64. 64 static const uint qt_meta_data_Object[] = {
  65. 65
  66. 66 // content:
  67. 67 9, // revision
  68. 68 0, // classname
  69. 69 3, 14, // classinfo
  70. 70 7, 20, // methods
  71. 71 2, 89, // properties
  72. 72 1, 99, // enums/sets
  73. 73 2, 110, // constructors
  74. 74 0, // flags
  75. 75 2, // signalCount
  76. 76
  77. 77 // classinfo: key, value
  78. 78 1, 2,
  79. 79 3, 4,
  80. 80 5, 6,
  81. 81
  82. 82 // signals: name, argc, parameters, tag, flags, initial metatype offsets
  83. 83 7, 1, 62, 8, 0x06, 2 /* Public */,
  84. 84 10, 1, 65, 8, 0x06, 4 /* Public */,
  85. 85
  86. 86 // slots: name, argc, parameters, tag, flags, initial metatype offsets
  87. 87 12, 1, 68, 8, 0x0a, 6 /* Public */,
  88. 88 13, 1, 71, 8, 0x0a, 8 /* Public */,
  89. 89
  90. 90 // methods: name, argc, parameters, tag, flags, initial metatype offsets
  91. 91 14, 1, 74, 8, 0x02, 10 /* Public */,
  92. 92 11, 0, 77, 8, 0x02, 12 /* Public */,
  93. 93 15, 1, 78, 8, 0x02, 13 /* Public */,
  94. 94
  95. 95 // signals: parameters
  96. 96 QMetaType::Void, QMetaType::Int, 9,
  97. 97 QMetaType::Void, QMetaType::Int, 11,
  98. 98
  99. 99 // slots: parameters
  100. 100 QMetaType::Void, QMetaType::Int, 9,
  101. 101 QMetaType::Void, QMetaType::Int, 11,
  102. 102
  103. 103 // methods: parameters
  104. 104 QMetaType::Void, QMetaType::Int, 9,
  105. 105 QMetaType::Int,
  106. 106 QMetaType::Void, QMetaType::Int, 11,
  107. 107
  108. 108 // constructors: parameters
  109. 109 0x80000000 | 8, QMetaType::QString, QMetaType::QObjectStar, 16, 17,
  110. 110 0x80000000 | 8, QMetaType::QString, 16,
  111. 111
  112. 112 // properties: name, type, flags
  113. 113 9, QMetaType::Int, 0x00015103, uint(0), 0,
  114. 114 11, QMetaType::Int, 0x00015103, uint(1), 0,
  115. 115
  116. 116 // enums: name, alias, flags, count, data
  117. 117 18, 18, 0x0, 3, 104,
  118. 118
  119. 119 // enum data: key, value
  120. 120 19, uint(Object::Basic),
  121. 121 20, uint(Object::Middle),
  122. 122 21, uint(Object::Advanced),
  123. 123
  124. 124 // constructors: name, argc, parameters, tag, flags, initial metatype offsets
  125. 125 0, 2, 81, 8, 0x0e, 15 /* Public */,
  126. 126 0, 1, 86, 8, 0x2e, 17 /* Public | MethodCloned */,
  127. 127
  128. 128 0 // eod
  129. 129 };
  130. 130
  131. 131 void Object::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
  132. 132 {
  133. 133 if (_c == QMetaObject::CreateInstance) {
  134. 134 switch (_id) {
  135. 135 case 0: { Object *_r = new Object((*reinterpret_cast< QString(*)>(_a[1])),(*reinterpret_cast< QObject*(*)>(_a[2])));
  136. 136 if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break;
  137. 137 case 1: { Object *_r = new Object((*reinterpret_cast< QString(*)>(_a[1])));
  138. 138 if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break;
  139. 139 default: break;
  140. 140 }
  141. 141 } else if (_c == QMetaObject::InvokeMetaMethod) {
  142. 142 auto *_t = static_cast<Object *>(_o);
  143. 143 (void)_t;
  144. 144 switch (_id) {
  145. 145 case 0: _t->ageChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
  146. 146 case 1: _t->scoreChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
  147. 147 case 2: _t->onAgeChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
  148. 148 case 3: _t->onScoreChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
  149. 149 case 4: _t->setAge((*reinterpret_cast< const int(*)>(_a[1]))); break;
  150. 150 case 5: { int _r = _t->score();
  151. 151 if (_a[0]) *reinterpret_cast< int*>(_a[0]) = std::move(_r); } break;
  152. 152 case 6: _t->setScore((*reinterpret_cast< const int(*)>(_a[1]))); break;
  153. 153 default: ;
  154. 154 }
  155. 155 } else if (_c == QMetaObject::IndexOfMethod) {
  156. 156 int *result = reinterpret_cast<int *>(_a[0]);
  157. 157 {
  158. 158 using _t = void (Object::*)(int );
  159. 159 if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Object::ageChanged)) {
  160. 160 *result = 0;
  161. 161 return;
  162. 162 }
  163. 163 }
  164. 164 {
  165. 165 using _t = void (Object::*)(int );
  166. 166 if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Object::scoreChanged)) {
  167. 167 *result = 1;
  168. 168 return;
  169. 169 }
  170. 170 }
  171. 171 }
  172. 172 #ifndef QT_NO_PROPERTIES
  173. 173 else if (_c == QMetaObject::ReadProperty) {
  174. 174 auto *_t = static_cast<Object *>(_o);
  175. 175 (void)_t;
  176. 176 void *_v = _a[0];
  177. 177 switch (_id) {
  178. 178 case 0: *reinterpret_cast< int*>(_v) = _t->age(); break;
  179. 179 case 1: *reinterpret_cast< int*>(_v) = _t->score(); break;
  180. 180 default: break;
  181. 181 }
  182. 182 } else if (_c == QMetaObject::WriteProperty) {
  183. 183 auto *_t = static_cast<Object *>(_o);
  184. 184 (void)_t;
  185. 185 void *_v = _a[0];
  186. 186 switch (_id) {
  187. 187 case 0: _t->setAge(*reinterpret_cast< int*>(_v)); break;
  188. 188 case 1: _t->setScore(*reinterpret_cast< int*>(_v)); break;
  189. 189 default: break;
  190. 190 }
  191. 191 } else if (_c == QMetaObject::ResetProperty) {
  192. 192 } else if (_c == QMetaObject::BindableProperty) {
  193. 193 }
  194. 194 #endif // QT_NO_PROPERTIES
  195. 195 }
  196. 196
  197. 197 const QMetaObject Object::staticMetaObject = { {
  198. 198 QMetaObject::SuperData::link<QObject::staticMetaObject>(),
  199. 199 qt_meta_stringdata_Object.offsetsAndSize,
  200. 200 qt_meta_data_Object,
  201. 201 qt_static_metacall,
  202. 202 nullptr,
  203. 203 qt_incomplete_metaTypeArray<qt_meta_stringdata_Object_t
  204. 204 , QtPrivate::TypeAndForceComplete<int, std::true_type>, QtPrivate::TypeAndForceComplete<int, std::true_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>
  205. 205 , QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>
  206. 206 , QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<const int &, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<const int &, std::false_type>
  207. 207 , QtPrivate::TypeAndForceComplete<QString, std::false_type>, QtPrivate::TypeAndForceComplete<QObject *, std::false_type>, QtPrivate::TypeAndForceComplete<QString, std::false_type>
  208. 208 >,
  209. 209 nullptr
  210. 210 } };
  211. 211
  212. 212
  213. 213 const QMetaObject *Object::metaObject() const
  214. 214 {
  215. 215 return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
  216. 216 }
  217. 217
  218. 218 void *Object::qt_metacast(const char *_clname)
  219. 219 {
  220. 220 if (!_clname) return nullptr;
  221. 221 if (!strcmp(_clname, qt_meta_stringdata_Object.stringdata0))
  222. 222 return static_cast<void*>(this);
  223. 223 return QObject::qt_metacast(_clname);
  224. 224 }
  225. 225
  226. 226 int Object::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
  227. 227 {
  228. 228 _id = QObject::qt_metacall(_c, _id, _a);
  229. 229 if (_id < 0)
  230. 230 return _id;
  231. 231 if (_c == QMetaObject::InvokeMetaMethod) {
  232. 232 if (_id < 7)
  233. 233 qt_static_metacall(this, _c, _id, _a);
  234. 234 _id -= 7;
  235. 235 } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
  236. 236 if (_id < 7)
  237. 237 *reinterpret_cast<QMetaType *>(_a[0]) = QMetaType();
  238. 238 _id -= 7;
  239. 239 }
  240. 240 #ifndef QT_NO_PROPERTIES
  241. 241 else if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty
  242. 242 || _c == QMetaObject::ResetProperty || _c == QMetaObject::BindableProperty
  243. 243 || _c == QMetaObject::RegisterPropertyMetaType) {
  244. 244 qt_static_metacall(this, _c, _id, _a);
  245. 245 _id -= 2;
  246. 246 }
  247. 247 #endif // QT_NO_PROPERTIES
  248. 248 return _id;
  249. 249 }
  250. 250
  251. 251 // SIGNAL 0
  252. 252 void Object::ageChanged(int _t1)
  253. 253 {
  254. 254 void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };
  255. 255 QMetaObject::activate(this, &staticMetaObject, 0, _a);
  256. 256 }
  257. 257
  258. 258 // SIGNAL 1
  259. 259 void Object::scoreChanged(int _t1)
  260. 260 {
  261. 261 void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };
  262. 262 QMetaObject::activate(this, &staticMetaObject, 1, _a);
  263. 263 }
  264. 264 QT_WARNING_POP
  265. 265 QT_END_MOC_NAMESPACE

QMetaObject 的主要数据

  1. 1 struct Q_CORE_EXPORT QMetaObject
  2. 2 {
  3. 3 ...
  4. 4 struct Data { // private data
  5. 5 SuperData superdata;
  6. 6 const uint *stringdata;
  7. 7 const uint *data;
  8. 8 typedef void (*StaticMetacallFunction)(QObject *,
  9. 9 QMetaObject::Call, int, void **);
  10. 10 StaticMetacallFunction static_metacall;
  11. 11 const SuperData *relatedMetaObjects;
  12. 12 const QtPrivate::QMetaTypeInterface *const *metaTypes;
  13. 13 void *extradata; //reserved for future use
  14. 14 } d;
  15. 15 ...
  16. 16 }

struct qt_meta_stringdata_Object_t {
  const uint offsetsAndSize[44];
  char stringdata0[167];
};

  1.  

44对应 多少项 ,有22个QT_MOC_LITERAL宏,展开之后有44项,记录了类的所有元对象信息。 第一个宏代表的是类名,offsetof 用来查询结构体内的成员的偏移地址,类名Object的偏移地址是4*44 = 176, 6代表Object的长度。依次类推,注意QT_MOC_LITERAL(59, 0) 是个空,这是由构造函数前的宏造成的,Q_INVOKABLE,会被记录元信息,至于这个空代表什么,目前不知道啥意思。stringdata0[167] 则是这些字符串的数量。

  接下来看下qt_meta_data_Object[],在看这个之前,先给出QMetaObjectPrivate的定义:

  1. struct QMetaObjectPrivate
  2. {
  3. // revision 7 is Qt 5.0 everything lower is not supported
  4. // revision 8 is Qt 5.12: It adds the enum name to QMetaEnum
  5. // revision 9 is Qt 6.0: It adds the metatype of properties and methods
  6. enum { OutputRevision = 9 }; // Used by moc, qmetaobjectbuilder and qdbus
  7. enum { IntsPerMethod = QMetaMethod::Data::Size };
  8. enum { IntsPerEnum = QMetaEnum::Data::Size };
  9. enum { IntsPerProperty = QMetaProperty::Data::Size };
  10.  
  11. int revision;
  12. int className;
  13. int classInfoCount, classInfoData;
  14. int methodCount, methodData;
  15. int propertyCount, propertyData;
  16. int enumeratorCount, enumeratorData;
  17. int constructorCount, constructorData;
  18. int flags;
  19. int signalCount;
  20. ...........
  21. }

qt_meta_data_Object[]中的第一项9,代表版本,QT6.0,第2项0,对应qt_meta_stringdata_Object[0] ,恰好对应类名,14对应本身数组qt_meta_data_Object[14]的位置:

// classinfo: key, value
1, 2, //stringdata0[167], 猜测第一个\0对应的Author为key,以及第二个\0对应的Scorpio为value.
3, 4,
5, 6,

8对应8个成员方法,20同理对应位置qt_meta_data_Object[20], 8个方法由2个signals, 2个slots, 4个methods组成。

2, 97 //对应两个属性,数组97项所在的位置;

1, 107// 对应一个enum:  Level,  数组107所在的位置.

3,  118, // constructors 。3对应3种不同的传参方法,因为俩个都是默认构造函数

0,       // flags 这个不知道啥意思。

2,       // signalCount 两个信号;

  1. // signals: name, argc, parameters, tag, flags, initial metatype offsets
  2. 7, 1, 68, 8, 0x06, 2 /* Public */,
  3. 10, 1, 71, 8, 0x06, 4 /* Public */,

7对应第7个\0对应的ageChanged, 1个参数, paremeters对应68不知道什么意思,tag,flags,offsets目前都不知道,估计只能看qmoc源码才能知道了。

剩下的大概自己过一眼了解下。太细究的话也得不到什么好处。

  1. const QMetaObject Object::staticMetaObject = { {
  2. QMetaObject::SuperData::link<QObject::staticMetaObject>(),
  3. qt_meta_stringdata_Object.offsetsAndSize,
  4. qt_meta_data_Object,
  5. qt_static_metacall,
  6. nullptr,
  7. qt_incomplete_metaTypeArray<qt_meta_stringdata_Object_t
  8. , QtPrivate::TypeAndForceComplete<int, std::true_type>, QtPrivate::TypeAndForceComplete<int, std::true_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>
  9. , QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>
  10. , QtPrivate::TypeAndForceComplete<int, std::false_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<const int &, std::false_type>, QtPrivate::TypeAndForceComplete<int, std::false_type>, QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<const int &, std::false_type>
  11. , QtPrivate::TypeAndForceComplete<QString, std::false_type>, QtPrivate::TypeAndForceComplete<QObject *, std::false_type>, QtPrivate::TypeAndForceComplete<QString, std::false_type>
  12. >,
  13. nullptr
  14. } };

这个staticMetaObject便是Q_Object宏里定义的,是个静态变量,程序运行时会首先初始化它。

  1. struct Data { // private data
  2. SuperData superdata;
  3. const uint *stringdata;
  4. const uint *data;
  5. typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
  6. StaticMetacallFunction static_metacall;
  7. const SuperData *relatedMetaObjects;
  8. const QtPrivate::QMetaTypeInterface *const *metaTypes;
  9. void *extradata; //reserved for future use
  10. } d;

以上是QMetaObject的数据定义,可以看出实际是在初始化d,  superdata 是基类QOjbect的staticMetaobject,

  1. SuperData superdata : QOjbectstaticMetaobject
  1. const uint *stringdata: qt_meta_stringdata_Object.offsetsAndSize// 并没有直接指向stringdata0[167]。
  1. const uint *data qt_meta_data_Object
  1. StaticMetacallFunction static_metacall: qt_static_metacall
  1. const SuperData *relatedMetaObjects: nullptr;
  1. const QtPrivate::QMetaTypeInterface *const *metaTypes: ....这个有点晕。模板元编程,萃取类型metaType信息
  1. extradatanullptr

  1. template<typename T>
  2. struct QMetaTypeInterfaceWrapper
  3. {
  4. static inline constexpr const QMetaTypeInterface metaType = {
  5. /*.revision=*/ 0,
  6. /*.alignment=*/ alignof(T),
  7. /*.size=*/ sizeof(T),
  8. /*.flags=*/ QMetaTypeTypeFlags<T>::Flags,
  9. /*.typeId=*/ BuiltinMetaType<T>::value,
  10. /*.metaObjectFn=*/ MetaObjectForType<T>::metaObjectFunction,
  11. /*.name=*/ QMetaTypeForType<T>::getName(),
  12. /*.defaultCtr=*/ QMetaTypeForType<T>::getDefaultCtr(),
  13. /*.copyCtr=*/ QMetaTypeForType<T>::getCopyCtr(),
  14. /*.moveCtr=*/ QMetaTypeForType<T>::getMoveCtr(),
  15. /*.dtor=*/ QMetaTypeForType<T>::getDtor(),
  16. /*.equals=*/ QEqualityOperatorForType<T>::equals,
  17. /*.lessThan=*/ QLessThanOperatorForType<T>::lessThan,
  18. /*.debugStream=*/ QDebugStreamOperatorForType<T>::debugStream,
  19. /*.dataStreamOut=*/ QDataStreamOperatorForType<T>::dataStreamOut,
  20. /*.dataStreamIn=*/ QDataStreamOperatorForType<T>::dataStreamIn,
  21. /*.legacyRegisterOp=*/ QMetaTypeForType<T>::getLegacyRegister()
  22. };
  23. };

咱也不知道它是干嘛的,模板元编程不咋会。

先来看下信号槽机制是什么样的。先看连接connect,

连接方式有多种:

static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);//典型的SIGNAL, SLOT宏模式

connect(this, &Object::ageChanged, this, &Object::onAgeChanged);调用的上述第一个,成员函数槽连接。

类型萃取成员函数。

先确认有Q_OGJECT 宏:

通过匹配test,确定匹配的项,模板的SFINAE技术,用test(...)是不是更容易看懂点。这种技术用的非常普遍,有需求可以参考一下。接下来确认参数是否匹配。这些都是在编译时期确定的

接下调用connectImpl,这个函数前4个参数都能看懂,主要看下第5个参数:

  1. new QtPrivate::QSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
  2. typename SignalType::ReturnType>(slot),
  1. template<typename Func, typename Args, typename R> class QSlotObject : public QSlotObjectBase
  2. {
  3. typedef QtPrivate::FunctionPointer<Func> FuncType;
  4. Func function;
  5. static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
  6. {
  7. switch (which) {
  8. case Destroy:
  9. delete static_cast<QSlotObject*>(this_);
  10. break;
  11. case Call:
  12. FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a);
  13. break;
  14. case Compare:
  15. *ret = *reinterpret_cast<Func *>(a) == static_cast<QSlotObject*>(this_)->function;
  16. break;
  17. case NumOperations: ;
  18. }
  19. }
  20. public:
  21. explicit QSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {}
  22. };

把slot传进去了,先简单看做是一个回调吧,一层一层的模板,太费劲。。。。List_left 得到的是个List<type1,type2...> 即参数类型

。 QSlotObject 保存了槽函数。接着看connectImpl.

  1. QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal,
  2. const QObject *receiver, void **slot,
  3. QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
  4. const int *types, const QMetaObject *senderMetaObject)
  5. {
  6. if (!signal) {
  7. qCWarning(lcConnect, "QObject::connect: invalid nullptr parameter");
  8. if (slotObj)
  9. slotObj->destroyIfLastRef();
  10. return QMetaObject::Connection();
  11. }
  12.  
  13. int signal_index = -1;
  14. void *args[] = { &signal_index, signal };
  15. for (; senderMetaObject && signal_index < 0; senderMetaObject = senderMetaObject->superClass()) {
  16. senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args);
  17. if (signal_index >= 0 && signal_index < QMetaObjectPrivate::get(senderMetaObject)->signalCount)
  18. break;
  19. }
  20. if (!senderMetaObject) {
  21. qCWarning(lcConnect, "QObject::connect: signal not found in %s", sender->metaObject()->className());
  22. slotObj->destroyIfLastRef();
  23. return QMetaObject::Connection(nullptr);
  24. }
  25. signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject);
  26. return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject);
  27. }

定义了一个*args[], 第一个参数是信号所对应的索引,第二个是信号的函数指针。使用发送方的senderMetaObject ,调用static_metacall。这是QmetaObject的内部函数,进而调用d->static_metacall,而这个static_metacall是个函数指针,保存的就是子类的qt_static_metacall,之前staticMetaObject对象初始化时,赋值进去的。qt_static_metacall对多种枚举类型做了处理,列举出来的枚举类型有:

enum Call {
InvokeMetaMethod,
ReadProperty,
WriteProperty,
ResetProperty,
CreateInstance,
IndexOfMethod,
RegisterPropertyMetaType,
RegisterMethodArgumentMetaType,
BindableProperty
};

这次传的是IndexOfMethod,会跟相应的信号类型去做对比,并填充args的第一个参数。

  1. else if (_c == QMetaObject::IndexOfMethod) {
  2. int *result = reinterpret_cast<int *>(_a[0]);
  3. {
  4. using _t = void (Object::*)(int );
  5. if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Object::ageChanged)) {
  6. *result = 0;
  7. return;
  8. }
  9. }
  10. {
  11. using _t = void (Object::*)(int );
  12. if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Object::scoreChanged)) {
  13. *result = 1;
  14. return;
  15. }
  16. }
  17. }

此时signal_index对应的只是在本类中的信号偏移。之后还会算上相对于父类信号的偏移。接下来调用:

QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject);

先对Qt::UniqueConnection类型的connect进行特殊处理,拿到对应的ConnectionData,这个数据类型包含所有的connect信息,再从中取出对应信号所对应的那些connections,毕竟一个信号可以连接多个,但uniqueConnection只能有一个,所以会去除这次多余的槽。

然后再记录是否是 Qt::SingleShotConnection。新建connection

这个connection记录了sender, 信号索引,threadData(这个应该是线程相关,以后再研究),receiver, 槽函数对象,就是那个QSlotObject,连接类型等。然后addConnection(signal_index, c.get())。

  1. void QObjectPrivate::addConnection(int signal, Connection *c)
  2. {
  3. Q_ASSERT(c->sender == q_ptr);
  4. ensureConnectionData();
  5. ConnectionData *cd = connections.loadRelaxed();
  6. cd->resizeSignalVector(signal + 1);
  7.  
  8. ConnectionList &connectionList = cd->connectionsForSignal(signal);
  9. if (connectionList.last.loadRelaxed()) {
  10. Q_ASSERT(connectionList.last.loadRelaxed()->receiver.loadRelaxed());
  11. connectionList.last.loadRelaxed()->nextConnectionList.storeRelaxed(c);
  12. } else {
  13. connectionList.first.storeRelaxed(c);
  14. }
  15. c->id = ++cd->currentConnectionId;
  16. c->prevConnectionList = connectionList.last.loadRelaxed();
  17. connectionList.last.storeRelaxed(c);
  18.  
  19. QObjectPrivate *rd = QObjectPrivate::get(c->receiver.loadRelaxed());
  20. rd->ensureConnectionData();
  21.  
  22. c->prev = &(rd->connections.loadRelaxed()->senders);
  23. c->next = *c->prev;
  24. *c->prev = c;
  25. if (c->next)
  26. c->next->prev = &c->next;
  27. }

拿到connectionData, 取出信号对应connectList,这是一个链表结构,去串接这个链表。同时也会更新recevier的connections,好像每次加入的connection会在链表头部。这样此次连接就保存下来了。

之后拿到QMetaMethod, moc产生的文件记录了相应的元信息qt_meta_data_Object,以此来创建metaMethod.

d.data对应的是qt_meta_data_Object[137],methodData是20。size是6,每个信号有6个元信息参数,以此判断对应的信号信息。

再看QMetaMethod data定义:

刚好对应上。s->connectNotify(method);调用了这个method,发现这个在基类里是空实现,看来子类要重写这个函数才能起到作用,可以在信号连接时做一些回调工作。至此完成了所有的连接工作。至于其他的几个连接大概也差不了太多。非成员函数的槽注意一下。

再来看看槽是如何被触发的,顺便把属性设置一起看一下。通过调用setProperty(age),看看如何实现的。

  1. bool QObject::setProperty(const char *name, const QVariant &value)
  2. {
  3. Q_D(QObject);
  4. const QMetaObject *meta = metaObject();
  5. if (!name || !meta)
  6. return false;
  7.  
  8. int id = meta->indexOfProperty(name);
  9. if (id < 0) {
  10. if (!d->extraData)
  11. d->extraData = new QObjectPrivate::ExtraData;
  12.  
  13. const int idx = d->extraData->propertyNames.indexOf(name);
  14.  
  15. if (!value.isValid()) {
  16. if (idx == -1)
  17. return false;
  18. d->extraData->propertyNames.removeAt(idx);
  19. d->extraData->propertyValues.removeAt(idx);
  20. } else {
  21. if (idx == -1) {
  22. d->extraData->propertyNames.append(name);
  23. d->extraData->propertyValues.append(value);
  24. } else {
  25. if (value.userType() == d->extraData->propertyValues.at(idx).userType()
  26. && value == d->extraData->propertyValues.at(idx))
  27. return false;
  28. d->extraData->propertyValues[idx] = value;
  29. }
  30. }
  31.  
  32. QDynamicPropertyChangeEvent ev(name);
  33. QCoreApplication::sendEvent(this, &ev);
  34.  
  35. return false;
  36. }
  37. QMetaProperty p = meta->property(id);
  38. #ifndef QT_NO_DEBUG
  39. if (!p.isWritable())
  40. qWarning("%s::setProperty: Property \"%s\" invalid,"
  41. " read-only or does not exist", metaObject()->className(), name);
  42. #endif
  43. return p.write(this, value);
  44. }

setProperty

首先拿到metaobject, 调用indexOfProperty(name)。里面实现就不细究了,创建了一个QMetaProperty,去与name(age)相比较,返回属性的索引,不过一样会加上父类的偏移。如果没有就会创建一个动态属性(前提是有DynamicMetaObject)。如果还没拿到,可以看到先判断有没有extraData, 没有就创建一个。并将这个属性加入extraData。接着调用meta->property(id),其实就是在里面构建了一个QMetaProperty。顺便展示一下QMetaProperty的数据结构:

跟QMetaMethod差不多,相同的套路。再调用p.write(this, value):

  1. bool QMetaProperty::write(QObject *object, const QVariant &value) const
  2. {
  3. if (!object || !isWritable())
  4. return false;
  5.  
  6. QVariant v = value;
  7. QMetaType t(mobj->d.metaTypes[data.index(mobj)]);
  8. if (t != QMetaType::fromType<QVariant>() && t != v.metaType()) {
  9. if (isEnumType() && !t.metaObject() && v.metaType().id() == QMetaType::QString) {
  10. // Assigning a string to a property of type Q_ENUMS (instead of Q_ENUM)
  11. bool ok;
  12. if (isFlagType())
  13. v = QVariant(menum.keysToValue(value.toByteArray(), &ok));
  14. else
  15. v = QVariant(menum.keyToValue(value.toByteArray(), &ok));
  16. if (!ok)
  17. return false;
  18. } else if (!value.isValid()) {
  19. if (isResettable())
  20. return reset(object);
  21. v = QVariant(t, nullptr);
  22. } else if (!v.convert(t)) {
  23. return false;
  24. }
  25. }
  26. // the status variable is changed by qt_metacall to indicate what it did
  27. // this feature is currently only used by Qt D-Bus and should not be depended
  28. // upon. Don't change it without looking into QDBusAbstractInterface first
  29. // -1 (unchanged): normal qt_metacall, result stored in argv[0]
  30. // changed: result stored directly in value, return the value of status
  31. int status = -1;
  32. // the flags variable is used by the declarative module to implement
  33. // interception of property writes.
  34. int flags = 0;
  35. void *argv[] = { nullptr, &v, &status, &flags };
  36. if (t == QMetaType::fromType<QVariant>())
  37. argv[0] = &v;
  38. else
  39. argv[0] = v.data();
  40. if (priv(mobj->d.data)->flags & PropertyAccessInStaticMetaCall && mobj->d.static_metacall)
  41. mobj->d.static_metacall(object, QMetaObject::WriteProperty, data.index(mobj), argv);
  42. else
  43. QMetaObject::metacall(object, QMetaObject::WriteProperty, data.index(mobj) + mobj->propertyOffset(), argv);
  44.  
  45. return status;
  46. }

QMetaProperty::write

这个metaTypes似乎有点眼熟,就是前面moc文件一堆模板创建的。判断传进来的variant的类型是否与属性相同。这个metaTypes怎么构建的还是不清楚。PropertyAccessInStaticMetaCall这个标志目前也不知道是什么,

enum MetaObjectFlag {
DynamicMetaObject = 0x01,
RequiresVariantMetaObject = 0x02,
PropertyAccessInStaticMetaCall = 0x04 // since Qt 5.5, property code is in the static metacall
};

接着往下看,最终还是调到qt_metacall。qt_static_metacall,找到对应的函数setAge。至此完成setProperty的任务。

在setAge里发送了一个ageChaged信号。实际上会调到moc文件里。其实agechaned(age)本来就是个函数,忽略emit即可,虽然我们只申明了该函数,但moc会生成它的实现。

std::addressof目的是当存在operator&的重载时, 依然能够获得变量的地址。再看activate里干了什么。

0是在本类里的信号偏移。之后会加上父类的偏移。调用doActivate<false>(sender, signal_index, argv);

该函数里面实现很复杂,主要的实现流程:拿到connect时构建的connectionData, 取出信号所对应的连接列表,判断发送信号的线程和当前线程是否相同。并确保在信号发射期间添加的信号不会被触发。之后遍历这个连接列表,获取对应的receiver。接着判断连接类型。以及是否是singleShot,是否已断开连接。再判断是成员函数槽调用,还是普通函数调用。成员函数调用如下实现:

构建了一个QSlotObjectBase,并调用call函数,这个QSlotObject就是我们在connect时创建的。可以往前看,

FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a);

最终会调用到该成员函数,中间全是模板,不在此深究。调用完所有的receiver后,做一些收尾工作。

可以看到所有的机制都归功于moc 生成的元信息数据。后续有时间再看其他的实现。

QT6 源码杂记的更多相关文章

  1. 【nodejs原理&源码杂记(8)】Timer模块与基于二叉堆的定时器

    [摘要] timers模块部分源码和定时器原理 示例代码托管在:http://www.github.com/dashnowords/blogs 一.概述 Timer模块相关的逻辑较为复杂,不仅包含Ja ...

  2. 【nodejs原理&源码杂记(8)】Timer模块与基于二叉堆的定时器

    目录 一.概述 二. 数据结构 2.1 链表 2.2 二叉堆 三. 从setTimeout理解Timer模块源码 3.1 timers.js中的定义 3.2 Timeout类定义 3.3 active ...

  3. C# DateTime的11种构造函数 [Abp 源码分析]十五、自动审计记录 .Net 登陆的时候添加验证码 使用Topshelf开发Windows服务、记录日志 日常杂记——C#验证码 c#_生成图片式验证码 C# 利用SharpZipLib生成压缩包 Sql2012如何将远程服务器数据库及表、表结构、表数据导入本地数据库

    C# DateTime的11种构造函数   别的也不多说没直接贴代码 using System; using System.Collections.Generic; using System.Glob ...

  4. React事件杂记及源码分析

    前提 最近通过阅读React官方文档的事件模块,发现了其主要提到了以下三个点  调用方法时需要手动绑定this  React事件是一种合成事件SyntheticEvent,什么是合成事件?  事件属性 ...

  5. TensorFlow源码框架 杂记

    一.为什么我们需要使用线程池技术(ThreadPool) 线程:采用“即时创建,即时销毁”策略,即接受请求后,创建一个新的线程,执行任务,完毕后,线程退出: 线程池:应用软件启动后,立即创建一定数量的 ...

  6. 【实习记】2014-09-04浏览代码查middle资料+总结我折腾过的源码浏览器

        浏览着代码,看源码可以先看make文件,make文件有制造的流程信息. 一般可以从运行的程序对应的cpp看起.然而如果有框架,那就不容易了,会关系错纵复杂. 总结一下我折腾过的源码阅读器. s ...

  7. HeartBeat源码安装

    只是写了安装流程,具体信息查看互联网; 环境: CentOS6.8 x86_64 min Heartbeat 3.0.6 http://hg.linux-ha.org/heartbeat-STABLE ...

  8. Python源码读后小结

    Python 笔记 前言(还是叫杂记吧) 在python中一切皆对象, python中的对象体系大致包含了"类型对象", "Mapping对象(dict)", ...

  9. epoll(2) 使用及源码分析的引子

    epoll(2) 使用及源码分析的引子 本文代码取自内核版本 4.17 epoll(2) - I/O 事件通知设施. epoll 是内核在2.6版本后实现的,是对 select(2)/poll(2) ...

随机推荐

  1. Jmeter压测学习5---HTTP Cookie管理器

    我司项目暂时不需要,直接转载:https://www.cnblogs.com/yoyoketang/p/11963342.html 前言 web网站的请求大部分都有cookies,jmeter的HTT ...

  2. Erase-Remove 惯用法

    看到<Effective STL>条款 9 的时候想到了我以前复习的"如何正确使用迭代器删除元素",我面试时使用的也是里面的方法,看面试官的反应好像也没有什么问题,还问 ...

  3. Python setattr() 函数 ,Python super() 函数: Python 内置函数 Python 内置函数

    描述 setattr 函数对应函数 getatt(),用于设置属性值,该属性必须存在. 语法 setattr 语法: setattr(object, name, value) 参数 object -- ...

  4. 这两个基础seo插件,wordpress网站必装

    WordPress对搜索引擎非常友好,这一点很多人都知道.不过我们在制作完成WordPress主题后,还可以在原来的良好基础上,添加两个队seo非常有利的WordPress插件. 第一个插件:Baid ...

  5. 使用gitlab runner进行CI(三):使用sonarqube做c++的静态检查

    目录 1. gitlab-ci.yml的配置 1.1 几个基本概念 1.2 使用CI进行代码检查demo 2. Sonarqube安装和配置 2.1 Sonarqube安装 2.2 数据库配置 2.3 ...

  6. Spring自动装配歧义性笔记

    Spring自动装配歧义性笔记 如果系统中存在两个都实现了同一接口的类,Spring在进行@Autowired自动装配的时候,会选择哪一个?如下: // 一下两个类均被标记为bean @Compone ...

  7. 你了解一条sql的执行顺序吗

    sql是后端开发人员经常碰到的问题,我们经常会写这样的sql:select name,id from student where id=12 order by id desc,把这条sql放到数据库中 ...

  8. Editing Tools(编辑工具)

    编辑工具 # Process: 修剪线 arcpy.TrimLine_edit("", "", "DELETE_SHORT") # Proc ...

  9. 一个简单的单例模式Demo

    /** * @author :nx014924 * @date :Created in 5/30/2021 1:09 PM * @description: * @modified By: * @ver ...

  10. Java(33)IO流的介绍&字节流

    作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15228446.html 博客主页:https://www.cnblogs.com/testero ...