文章目錄
- QMetaObject::className()
- QMetaObject::superClass()
- QMetaObject::classInfoCount()
- QMetaObject::classInfoOffset ()
- QMetaObject::classInfo (int index)
- QMetaObject::indexOfClassInfo ()
- int constructorCount () const
- QMetaMethod constructor ( int index ) const
- int indexOfConstructor ( const char * constructor ) const
- intindexOfSignal ( const char * signal ) const
- QMetaPropertyproperty ( int index ) const
- QMetaPropertyuserProperty () const
本篇從Qt MetaObject原始碼解讀相關介面的實現,這些介面都定義於qmetaobject.cpp中。
QMetaObject::className()
inline const char *QMetaObject::className() const
{ return d.stringdata; }
從前一篇可知,d.stringdata就是那塊字串資料,包含若干c字串(以'/0')結尾。如果把d.stringdata當做一個c字串指標的話,就是這個字串序列的第一個字串,正是類名。
QMetaObject::superClass()
inline const QMetaObject *QMetaObject::superClass() const
{ return d.superdata; }
QMetaObject::classInfoCount()
int QMetaObject::classInfoCount() const
{
int n = priv(d.data)->classInfoCount;
const QMetaObject *m = d.superdata;
while (m) {
n += priv(m->d.data)->classInfoCount;
m = m->d.superdata;
}
return n;
}
從代碼可以看出,返回該類的所有classinfo數目,包括所有基類的。
函數priv是一個簡單inline函數:
static inline const QMetaObjectPrivate *priv(const uint* data)
{ return reinterpret_cast<const QMetaObjectPrivate*>(data); }
由前一篇可知,d.data指向的是那塊二進位資訊,priv將d.data解釋為QMetaObjectPrivate。
QMetaObjectPrivate是QMetaObject的私人實作類別,其資料定義部分如下(見標頭檔qmetaobject_p.h)。和前一篇的樣本moc檔案內容一對應,其含義一目瞭然。
struct QMetaObjectPrivate
{
int revision;
int className;
int classInfoCount, classInfoData;
int methodCount, methodData;
int propertyCount, propertyData;
int enumeratorCount, enumeratorData;
int constructorCount, constructorData; //since revision 2
int flags; //since revision 3
int signalCount; //since revision
}
QMetaObject::classInfoOffset ()
int classInfoOffset () const
{
int offset = 0;
const QMetaObject *m = d.superdata;
while (m) {
offset += priv(m->d.data)->classInfoCount;
m = m->d.superdata;
}
return offset;
}
該類的含義是返回這個類所定義的classinfo的起始索引值,相當於它的祖先類所定義的classinfo的數量。
QMetaObject::classInfo (int index)
QMetaClassInfo classInfo ( int index ) const
{
int i = index;
i -= classInfoOffset();
if (i < 0 && d.superdata)
return d.superdata->classInfo(index);
QMetaClassInfo result;
if (i >= 0 && i < priv(d.data)->classInfoCount) {
result.mobj = this;
result.handle = priv(d.data)->classInfoData + 2*i;
}
return result;
}
這個代碼的流程比較簡單。priv(d.data)->classInfoData是classinfo的資訊在d.data中的位移;每條classinfo資訊佔2個UINT的大小,因此“priv(d.data)->classInfoData + 2*i”這個運算式的值就是第i個classinfo的資訊在d.data中的位移。
QMetaObject::indexOfClassInfo ()
int indexOfClassInfo ( const char * name ) const
{
int i = -1;
const QMetaObject *m = this;
while (m && i < 0) {
for (i = priv(m->d.data)->classInfoCount-1; i >= 0; --i)
if (strcmp(name, m->d.stringdata
+ m->d.data[priv(m->d.data)->classInfoData + 2*i]) == 0) {
i += m->classInfoOffset();
break;
}
m = m->d.superdata;
}
return i;
}
按照繼承層次,從下往上尋找名字為name的classinfo。
參考前一函數的解釋,運算式m->d.data[priv(m->d.data)->classInfoData + 2*i]的值是第i個classinfo資訊的第一個32位值。該值是字元資訊塊d.stringdata中的索引值。因此m->d.stringdata+
m->d.data[priv(m->d.data)->classInfoData + 2*i]就是classinfo名稱的字串。
int constructorCount () const
int QMetaObject::constructorCount() const
{
if (priv(d.data)->revision < 2)
return 0;
return priv(d.data)->constructorCount;
}
QMetaMethod constructor ( int index ) const
QMetaMethod QMetaObject::constructor(int index) const
{
int i = index;
QMetaMethod result;
if (priv(d.data)->revision >= 2 && i >= 0 && i < priv(d.data)->constructorCount) {
result.mobj = this;
result.handle = priv(d.data)->constructorData + 5*i;
}
return result;
}
int indexOfConstructor ( const char * constructor ) const
int QMetaObject::indexOfConstructor(const char *constructor) const
{
if (priv(d.data)->revision < 2)
return -1;
for (int i = priv(d.data)->constructorCount-1; i >= 0; --i) {
if (strcmp(constructor, d.stringdata
+ d.data[priv(d.data)->constructorData + 5*i]) == 0) {
return i;
}
}
return -1;
}
int enumeratorCount () const
int enumeratorOffset () const
QMetaEnum enumerator ( int index ) const
int indexOfEnumerator ( const char * name ) const
這組函數與classinfo那一組的實現及其相似。
intmethodCount () const 略;
intmethodOffset () const 略;
QMetaMethodmethod ( int index ) const
{
int i = index;
i -= methodOffset();
if (i < 0 && d.superdata)
return d.superdata->method(index);
QMetaMethod result;
if (i >= 0 && i < priv(d.data)->methodCount) {
result.mobj = this;
result.handle = priv(d.data)->methodData + 5*i;
}
return result;
}
該函數的實現方式也一目瞭然。
intindexOfMethod ( const char * method ) const 略;
intindexOfSignal ( const char * signal ) const
{
const QMetaObject *m = this;
int i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal);
if (i >= 0)
i += m->methodOffset();
return i;
}
int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, const char *signal)
{
int i = -1;
while (*baseObject) {
const QMetaObject *const m = *baseObject;
for (i = priv(m->d.data)->methodCount-1; i >= 0; --i)
if ((m->d.data[priv(m->d.data)->methodData + 5*i + 4] & MethodTypeMask) == MethodSignal
&& strcmp(signal, m->d.stringdata
+ m->d.data[priv(m->d.data)->methodData + 5*i]) == 0) {
break;
}
if (i >= 0)
break;
*baseObject = m->d.superdata;
}
}
可以看出,尋找signal的特別之處在於,通過method中繼資料的第五項來判斷這是不是一個signal。
intindexOfSlot ( const char * slot ) const 略;
intpropertyCount () const 略;
intpropertyOffset () const 略;
intindexOfProperty ( const char * name ) const 略;
QMetaPropertyproperty ( int index ) const
{
int i = index;
i -= propertyOffset();
if (i < 0 && d.superdata)
return d.superdata->property(index);
QMetaProperty result;
if (i >= 0 && i < priv(d.data)->propertyCount) {
int handle = priv(d.data)->propertyData + 3*i;
int flags = d.data[handle + 2];
const char *type = d.stringdata + d.data[handle + 1];
result.mobj = this;
result.handle = handle;
result.idx = i;
if (flags & EnumOrFlag) {
result.menum = enumerator(indexOfEnumerator(type));
if (!result.menum.isValid()) {
QByteArray enum_name = type;
QByteArray scope_name = d.stringdata;
int s = enum_name.lastIndexOf("::");
if (s > 0) {
scope_name = enum_name.left(s);
enum_name = enum_name.mid(s + 2);
}
const QMetaObject *scope = 0;
if (scope_name == "Qt")
scope = &QObject::staticQtMetaObject;
else
scope = QMetaObject_findMetaObject(this, scope_name);
if (scope)
result.menum = scope->enumerator(scope->indexOfEnumerator(enum_name));
}
}
}
return result;
}
該函數的特別之處在於,如果這個propery是一個枚舉類型的話,就為傳回值QMetaPropery賦上正確QMetaEnum屬性值。
QMetaPropertyuserProperty () const
{
const int propCount = propertyCount();
for (int i = propCount - 1; i >= 0; --i) {
const QMetaProperty prop = property(i);
if (prop.isUser())
return prop;
}
return QMetaProperty();
}
從這個函數的實現來看,一個QObject應該只會有一個開啟USER flag的property。