kehuadong

qt元对象系统之 Q_OBJECT宏

宏展开是这样 

#define Q_OBJECT \
public: \
    QT_WARNING_PUSH \
    Q_OBJECT_NO_OVERRIDE_WARNING \
    static const QMetaObject staticMetaObject; \
    virtual const QMetaObject *metaObject() const; \
    virtual void *qt_metacast(const char *); \
    virtual int qt_metacall(QMetaObject::Call, int, void **); \
    QT_TR_FUNCTIONS \
private: \
    Q_OBJECT_NO_ATTRIBUTES_WARNING \
    Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
    QT_WARNING_POP \
    struct QPrivateSignal {}; \
    QT_ANNOTATE_CLASS(qt_qobject, "")

然后通过moc工具生成以下变量和函数的定义

静态对象     staticMetaObject

静态方法     qt_static_metacall

成员虚函数 metaObject, qt_meatacast, qt_metacall

 

例子

class Hello : public QObject
{
    Q_OBJECT
};

moc生成的内容

QT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_Hello_t {
    QByteArrayData data[1];
    char stringdata0[6];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
    qptrdiff(offsetof(qt_meta_stringdata_Hello_t, stringdata0) + ofs \
        - idx * sizeof(QByteArrayData)) \
    )
static const qt_meta_stringdata_Hello_t qt_meta_stringdata_Hello = {
    {
QT_MOC_LITERAL(0, 0, 5) // "Hello"

    },
    "Hello"
};
#undef QT_MOC_LITERAL

static const uint qt_meta_data_Hello[] = {

 // content:
       7,       // revision
       0,       // classname
       0,    0, // classinfo
       0,    0, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       0,       // signalCount

       0        // eod
};

void Hello::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    Q_UNUSED(_o);
    Q_UNUSED(_id);
    Q_UNUSED(_c);
    Q_UNUSED(_a);
}

const QMetaObject Hello::staticMetaObject = {
    { &QObject::staticMetaObject, qt_meta_stringdata_Hello.data,
      qt_meta_data_Hello,  qt_static_metacall, nullptr, nullptr}
};


const QMetaObject *Hello::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}

void *Hello::qt_metacast(const char *_clname)
{
    if (!_clname) return nullptr;
    if (!strcmp(_clname, qt_meta_stringdata_Hello.stringdata0))
        return static_cast<void*>(this);
    return QObject::qt_metacast(_clname);
}

int Hello::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
    _id = QObject::qt_metacall(_c, _id, _a);
    return _id;
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE

 QT_MOC_LITERAL用来初始化QByteArrayData结构体,

struct Q_CORE_EXPORT QArrayData
{
    QtPrivate::RefCount ref;
    int size;
    uint alloc : 31;
    uint capacityReserved : 1;

    qptrdiff offset; // in bytes from beginning of header

    void *data()
    {
        Q_ASSERT(size == 0
                || offset < 0 || size_t(offset) >= sizeof(QArrayData));
        return reinterpret_cast<char *>(this) + offset;
    }

    ....
};

Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, offset)展开后是{ Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, offset }

可以看到 QT_MOC_LITERAL(0, 0, 5) // "Hello"

代表Hello字符串长度5,  最终计算出的offset为字符串地址和用来定位Hello字符串地址所对应的QByteArrayData地址的偏移

注意: 这里只有一个Hello字符串,如果有n个字符串, QByteArrayData data[1]; 就会变成QByteArrayData data[n];

假如Hello字符串所对应的QByteArrayData为data[3], 那么 offset 就是 Hello地址与&data[3]的偏移

此后可以用该对应的QByteArrayData的data()方法取得Hello字符串

 

 qt_meta_data_Hello的头部为这个结构体

struct QMetaObjectPrivate
{
    // revision 7 is Qt 5.0 everything lower is not supported
    enum { OutputRevision = 7 }; // Used by moc, qmetaobjectbuilder and qdbus

    int revision;
    int className;
    int classInfoCount, classInfoData;
    int methodCount, methodData;
    int propertyCount, propertyData;
    int enumeratorCount, enumeratorData;
    int constructorCount, constructorData;
    int flags;
    int signalCount;
    ....
}

0,   0, // methods如果定义了信号函数或者槽函数, 第二个0就会变成14,  而第一个0则变为信槽总数

注意: 信号函数也是由moc工具自动生成的,而0,       // signalCount 中的0则变为信号函数总数

 

信号函数通常如下

// SIGNAL 0
void Hello::tellName(QString _t1)
{
    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

第三个参数是信号在本类中的本地信号索引, 最终会展开为此函数,

void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv)

其中signalOffset为计算出来的全部父类信号总数, 然后信号的绝对索引为 int signal_index = signalOffset + local_signal_index;

actiate函数的调用,导致所有连接上的槽函数被调用

 

posted on 2020-11-08 11:54  kehuadong  阅读(550)  评论(0编辑  收藏  举报

导航