温酒读Qt:QObject中篇2 ——欲遮还羞的 QObjectPrivate

news2024/11/17 6:22:49

《妙法莲华经》曰:“佛道长远,久受勤苦,乃可得成。” 世事修炼,莫不如是,日拱一卒无有尽,功不唐捐终入海。


传送门:
《温酒读Qt:QObject 序篇》
《温酒读Qt:QObject中篇1—— Q_OBJECT的隐秘角落》


1、QObjectPrivate class

先贴源码,然后我们挑重点进行分析;

/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef QOBJECT_P_H
#define QOBJECT_P_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists for the convenience
// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp.  This header
// file may change from version to version without notice, or even be removed.
//
// We mean it.
//

#include <QtCore/private/qglobal_p.h>
#include "QtCore/qobject.h"
#include "QtCore/qpointer.h"
#include "QtCore/qsharedpointer.h"
#include "QtCore/qcoreevent.h"
#include "QtCore/qlist.h"
#include "QtCore/qvector.h"
#include "QtCore/qvariant.h"
#include "QtCore/qreadwritelock.h"

QT_BEGIN_NAMESPACE

class QVariant;
class QThreadData;
class QObjectConnectionListVector;
namespace QtSharedPointer { struct ExternalRefCountData; }

/* for Qt Test */
struct QSignalSpyCallbackSet
{
    typedef void (*BeginCallback)(QObject *caller, int signal_or_method_index, void **argv);
    typedef void (*EndCallback)(QObject *caller, int signal_or_method_index);
    BeginCallback signal_begin_callback,
                    slot_begin_callback;
    EndCallback signal_end_callback,
                slot_end_callback;
};
void Q_CORE_EXPORT qt_register_signal_spy_callbacks(QSignalSpyCallbackSet *callback_set);

extern Q_CORE_EXPORT QBasicAtomicPointer<QSignalSpyCallbackSet> qt_signal_spy_callback_set;

enum { QObjectPrivateVersion = QT_VERSION };

class Q_CORE_EXPORT QAbstractDeclarativeData
{
public:
    static void (*destroyed)(QAbstractDeclarativeData *, QObject *);
    static void (*destroyed_qml1)(QAbstractDeclarativeData *, QObject *);
    static void (*parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *);
    static void (*signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **);
    static int  (*receivers)(QAbstractDeclarativeData *, const QObject *, int);
    static bool (*isSignalConnected)(QAbstractDeclarativeData *, const QObject *, int);
    static void (*setWidgetParent)(QObject *, QObject *); // Used by the QML engine to specify parents for widgets. Set by QtWidgets.
};

// This is an implementation of QAbstractDeclarativeData that is identical with
// the implementation in QtDeclarative and QtQml for the first bit
struct QAbstractDeclarativeDataImpl : public QAbstractDeclarativeData
{
    quint32 ownedByQml1:1;
    quint32 unused: 31;
};

class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{
    Q_DECLARE_PUBLIC(QObject)

public:
    struct ExtraData
    {
        ExtraData() {}
    #ifndef QT_NO_USERDATA
        QVector<QObjectUserData *> userData;
    #endif
        QList<QByteArray> propertyNames;
        QVector<QVariant> propertyValues;
        QVector<int> runningTimers;
        QList<QPointer<QObject> > eventFilters;
        QString objectName;
    };

    typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);
    struct Connection;
    struct SignalVector;

    struct ConnectionOrSignalVector {
        union {
            // linked list of orphaned connections that need cleaning up
            ConnectionOrSignalVector *nextInOrphanList;
            // linked list of connections connected to slots in this object
            Connection *next;
        };

        static SignalVector *asSignalVector(ConnectionOrSignalVector *c) {
            if (reinterpret_cast<quintptr>(c) & 1)
                return reinterpret_cast<SignalVector *>(reinterpret_cast<quintptr>(c) & ~quintptr(1u));
            return nullptr;
        }
        static Connection *fromSignalVector(SignalVector *v) {
            return reinterpret_cast<Connection *>(reinterpret_cast<quintptr>(v) | quintptr(1u));
        }
    };

    struct Connection : public ConnectionOrSignalVector
    {
        // linked list of connections connected to slots in this object, next is in base class
        Connection **prev;
        // linked list of connections connected to signals in this object
        QAtomicPointer<Connection> nextConnectionList;
        Connection *prevConnectionList;

        QObject *sender;
        QAtomicPointer<QObject> receiver;
        QAtomicPointer<QThreadData> receiverThreadData;
        union {
            StaticMetaCallFunction callFunction;
            QtPrivate::QSlotObjectBase *slotObj;
        };
        QAtomicPointer<const int> argumentTypes;
        QAtomicInt ref_;
        uint id = 0;
        ushort method_offset;
        ushort method_relative;
        int signal_index : 27; // In signal range (see QObjectPrivate::signalIndex())
        ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
        ushort isSlotObject : 1;
        ushort ownArgumentTypes : 1;
        Connection() : ref_(2), ownArgumentTypes(true) {
            //ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection
        }
        ~Connection();
        int method() const { Q_ASSERT(!isSlotObject); return method_offset + method_relative; }
        void ref() { ref_.ref(); }
        void freeSlotObject()
        {
            if (isSlotObject) {
                slotObj->destroyIfLastRef();
                isSlotObject = false;
            }
        }
        void deref() {
            if (!ref_.deref()) {
                Q_ASSERT(!receiver.loadRelaxed());
                Q_ASSERT(!isSlotObject);
                delete this;
            }
        }
    };
    // ConnectionList is a singly-linked list
    struct ConnectionList {
        QAtomicPointer<Connection> first;
        QAtomicPointer<Connection> last;
    };

    struct Sender
    {
        Sender(QObject *receiver, QObject *sender, int signal)
            : receiver(receiver), sender(sender), signal(signal)
        {
            if (receiver) {
                ConnectionData *cd = receiver->d_func()->connections.loadRelaxed();
                previous = cd->currentSender;
                cd->currentSender = this;
            }
        }
        ~Sender()
        {
            if (receiver)
                receiver->d_func()->connections.loadRelaxed()->currentSender = previous;
        }
        void receiverDeleted()
        {
            Sender *s = this;
            while (s) {
                s->receiver = nullptr;
                s = s->previous;
            }
        }
        Sender *previous;
        QObject *receiver;
        QObject *sender;
        int signal;
    };

    struct SignalVector : public ConnectionOrSignalVector {
        quintptr allocated;
        // ConnectionList signals[]
        ConnectionList &at(int i)
        {
            return reinterpret_cast<ConnectionList *>(this + 1)[i + 1];
        }
        const ConnectionList &at(int i) const
        {
            return reinterpret_cast<const ConnectionList *>(this + 1)[i + 1];
        }
        int count() const { return static_cast<int>(allocated); }
    };



    /*
        This contains the all connections from and to an object.

        The signalVector contains the lists of connections for a given signal. The index in the vector correspond
        to the signal index. The signal index is the one returned by QObjectPrivate::signalIndex (not
        QMetaObject::indexOfSignal). allsignals contains a list of special connections that will get invoked on
        any signal emission. This is done by connecting to signal index -1.

        This vector is protected by the object mutex (signalSlotLock())

        Each Connection is also part of a 'senders' linked list. This one contains all connections connected
        to a slot in this object. The mutex of the receiver must be locked when touching the pointers of this
        linked list.
    */
    struct ConnectionData {
        // the id below is used to avoid activating new connections. When the object gets
        // deleted it's set to 0, so that signal emission stops
        QAtomicInteger<uint> currentConnectionId;
        QAtomicInt ref;
        QAtomicPointer<SignalVector> signalVector;
        Connection *senders = nullptr;
        Sender *currentSender = nullptr;   // object currently activating the object
        QAtomicPointer<Connection> orphaned;

        ~ConnectionData()
        {
            deleteOrphaned(orphaned.loadRelaxed());
            SignalVector *v = signalVector.loadRelaxed();
            if (v)
                free(v);
        }

        // must be called on the senders connection data
        // assumes the senders and receivers lock are held
        void removeConnection(Connection *c);
        void cleanOrphanedConnections(QObject *sender)
        {
            if (orphaned.loadRelaxed() && ref.loadAcquire() == 1)
                cleanOrphanedConnectionsImpl(sender);
        }
        void cleanOrphanedConnectionsImpl(QObject *sender);

        ConnectionList &connectionsForSignal(int signal)
        {
            return signalVector.loadRelaxed()->at(signal);
        }

        void resizeSignalVector(uint size) {
            SignalVector *vector = this->signalVector.loadRelaxed();
            if (vector && vector->allocated > size)
                return;
            size = (size + 7) & ~7;
            SignalVector *newVector = reinterpret_cast<SignalVector *>(malloc(sizeof(SignalVector) + (size + 1) * sizeof(ConnectionList)));
            int start = -1;
            if (vector) {
                memcpy(newVector, vector, sizeof(SignalVector) + (vector->allocated + 1) * sizeof(ConnectionList));
                start = vector->count();
            }
            for (int i = start; i < int(size); ++i)
                newVector->at(i) = ConnectionList();
            newVector->next = nullptr;
            newVector->allocated = size;

            signalVector.storeRelaxed(newVector);
            if (vector) {
                vector->nextInOrphanList = orphaned.loadRelaxed();
                orphaned.storeRelaxed(ConnectionOrSignalVector::fromSignalVector(vector));
            }
        }
        int signalVectorCount() const {
            return  signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1;
        }

        static void deleteOrphaned(ConnectionOrSignalVector *c);
    };

    QObjectPrivate(int version = QObjectPrivateVersion);
    virtual ~QObjectPrivate();
    void deleteChildren();

    void setParent_helper(QObject *);
    void moveToThread_helper();
    void setThreadData_helper(QThreadData *currentData, QThreadData *targetData);
    void _q_reregisterTimers(void *pointer);

    bool isSender(const QObject *receiver, const char *signal) const;
    QObjectList receiverList(const char *signal) const;
    QObjectList senderList() const;

    void addConnection(int signal, Connection *c);

    static QObjectPrivate *get(QObject *o) {
        return o->d_func();
    }
    static const QObjectPrivate *get(const QObject *o) { return o->d_func(); }

    int signalIndex(const char *signalName, const QMetaObject **meta = nullptr) const;
    bool isSignalConnected(uint signalIdx, bool checkDeclarative = true) const;
    bool maybeSignalConnected(uint signalIndex) const;
    inline bool isDeclarativeSignalConnected(uint signalIdx) const;

    // To allow abitrary objects to call connectNotify()/disconnectNotify() without making
    // the API public in QObject. This is used by QQmlNotifierEndpoint.
    inline void connectNotify(const QMetaMethod &signal);
    inline void disconnectNotify(const QMetaMethod &signal);

    template <typename Func1, typename Func2>
    static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
                                                  const typename QtPrivate::FunctionPointer<Func2>::Object *receiverPrivate, Func2 slot,
                                                  Qt::ConnectionType type = Qt::AutoConnection);

    template <typename Func1, typename Func2>
    static inline bool disconnect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
                                  const typename QtPrivate::FunctionPointer<Func2>::Object *receiverPrivate, Func2 slot);

    static QMetaObject::Connection connectImpl(const QObject *sender, int signal_index,
                                               const QObject *receiver, void **slot,
                                               QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
                                               const int *types, const QMetaObject *senderMetaObject);
    static QMetaObject::Connection connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type);
    static bool disconnect(const QObject *sender, int signal_index, void **slot);

    void ensureConnectionData()
    {
        if (connections.loadRelaxed())
            return;
        ConnectionData *cd = new ConnectionData;
        cd->ref.ref();
        connections.storeRelaxed(cd);
    }
public:
    ExtraData *extraData;    // extra data set by the user
    QThreadData *threadData; // id of the thread that owns the object

    using ConnectionDataPointer = QExplicitlySharedDataPointer<ConnectionData>;
    QAtomicPointer<ConnectionData> connections;

    union {
        QObject *currentChildBeingDeleted; // should only be used when QObjectData::isDeletingChildren is set
        QAbstractDeclarativeData *declarativeData; //extra data used by the declarative module
    };

    // these objects are all used to indicate that a QObject was deleted
    // plus QPointer, which keeps a separate list
    QAtomicPointer<QtSharedPointer::ExternalRefCountData> sharedRefcount;
};

Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_MOVABLE_TYPE);

inline bool QObjectPrivate::isDeclarativeSignalConnected(uint signal_index) const
{
    return declarativeData && QAbstractDeclarativeData::isSignalConnected
            && QAbstractDeclarativeData::isSignalConnected(declarativeData, q_func(), signal_index);
}

inline void QObjectPrivate::connectNotify(const QMetaMethod &signal)
{
    q_ptr->connectNotify(signal);
}

inline void QObjectPrivate::disconnectNotify(const QMetaMethod &signal)
{
    q_ptr->disconnectNotify(signal);
}

namespace QtPrivate {
template<typename Func, typename Args, typename R> class QPrivateSlotObject : public QSlotObjectBase
{
    typedef QtPrivate::FunctionPointer<Func> FuncType;
    Func function;
    static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
    {
        switch (which) {
            case Destroy:
                delete static_cast<QPrivateSlotObject*>(this_);
                break;
            case Call:
                FuncType::template call<Args, R>(static_cast<QPrivateSlotObject*>(this_)->function,
                                                 static_cast<typename FuncType::Object *>(QObjectPrivate::get(r)), a);
                break;
            case Compare:
                *ret = *reinterpret_cast<Func *>(a) == static_cast<QPrivateSlotObject*>(this_)->function;
                break;
            case NumOperations: ;
        }
    }
public:
    explicit QPrivateSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {}
};
} //namespace QtPrivate

template <typename Func1, typename Func2>
inline QMetaObject::Connection QObjectPrivate::connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
                                                       const typename QtPrivate::FunctionPointer<Func2>::Object *receiverPrivate, Func2 slot,
                                                       Qt::ConnectionType type)
{
    typedef QtPrivate::FunctionPointer<Func1> SignalType;
    typedef QtPrivate::FunctionPointer<Func2> SlotType;
    Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
                      "No Q_OBJECT in the class with the signal");

    //compilation error if the arguments does not match.
    Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
                      "The slot requires more arguments than the signal provides.");
    Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
                      "Signal and slot arguments are not compatible.");
    Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
                      "Return type of the slot is not compatible with the return type of the signal.");

    const int *types = nullptr;
    if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
        types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();

    return QObject::connectImpl(sender, reinterpret_cast<void **>(&signal),
        receiverPrivate->q_ptr, reinterpret_cast<void **>(&slot),
        new QtPrivate::QPrivateSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
                                        typename SignalType::ReturnType>(slot),
        type, types, &SignalType::Object::staticMetaObject);
}

template <typename Func1, typename Func2>
bool QObjectPrivate::disconnect(const typename QtPrivate::FunctionPointer< Func1 >::Object* sender, Func1 signal,
                                const typename QtPrivate::FunctionPointer< Func2 >::Object* receiverPrivate, Func2 slot)
{
    typedef QtPrivate::FunctionPointer<Func1> SignalType;
    typedef QtPrivate::FunctionPointer<Func2> SlotType;
    Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
                      "No Q_OBJECT in the class with the signal");
    //compilation error if the arguments does not match.
    Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
                      "Signal and slot arguments are not compatible.");
    return QObject::disconnectImpl(sender, reinterpret_cast<void **>(&signal),
                          receiverPrivate->q_ptr, reinterpret_cast<void **>(&slot),
                          &SignalType::Object::staticMetaObject);
}

Q_DECLARE_TYPEINFO(QObjectPrivate::Connection, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QObjectPrivate::Sender, Q_MOVABLE_TYPE);

class QSemaphore;
class Q_CORE_EXPORT QAbstractMetaCallEvent : public QEvent
{
public:
    QAbstractMetaCallEvent(const QObject *sender, int signalId, QSemaphore *semaphore = nullptr)
        : QEvent(MetaCall), signalId_(signalId), sender_(sender)
#if QT_CONFIG(thread)
        , semaphore_(semaphore)
#endif
    { Q_UNUSED(semaphore); }
    ~QAbstractMetaCallEvent();

    virtual void placeMetaCall(QObject *object) = 0;

    inline const QObject *sender() const { return sender_; }
    inline int signalId() const { return signalId_; }

private:
    int signalId_;
    const QObject *sender_;
#if QT_CONFIG(thread)
    QSemaphore *semaphore_;
#endif
};

class Q_CORE_EXPORT QMetaCallEvent : public QAbstractMetaCallEvent
{
public:
    // blocking queued with semaphore - args always owned by caller
    QMetaCallEvent(ushort method_offset, ushort method_relative,
                   QObjectPrivate::StaticMetaCallFunction callFunction,
                   const QObject *sender, int signalId,
                   void **args, QSemaphore *semaphore);
    QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj,
                   const QObject *sender, int signalId,
                   void **args, QSemaphore *semaphore);

    // queued - args allocated by event, copied by caller
    QMetaCallEvent(ushort method_offset, ushort method_relative,
                   QObjectPrivate::StaticMetaCallFunction callFunction,
                   const QObject *sender, int signalId,
                   int nargs);
    QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj,
                   const QObject *sender, int signalId,
                   int nargs);

    ~QMetaCallEvent() override;

    inline int id() const { return d.method_offset_ + d.method_relative_; }
    inline const void * const* args() const { return d.args_; }
    inline void ** args() { return d.args_; }
    inline const int *types() const { return reinterpret_cast<int*>(d.args_ + d.nargs_); }
    inline int *types() { return reinterpret_cast<int*>(d.args_ + d.nargs_); }

    virtual void placeMetaCall(QObject *object) override;

private:
    inline void allocArgs();

    struct Data {
        QtPrivate::QSlotObjectBase *slotObj_;
        void **args_;
        QObjectPrivate::StaticMetaCallFunction callFunction_;
        int nargs_;
        ushort method_offset_;
        ushort method_relative_;
    } d;
    // preallocate enough space for three arguments
    char prealloc_[3*(sizeof(void*) + sizeof(int))];
};

class QBoolBlocker
{
    Q_DISABLE_COPY_MOVE(QBoolBlocker)
public:
    explicit inline QBoolBlocker(bool &b, bool value=true):block(b), reset(b){block = value;}
    inline ~QBoolBlocker(){block = reset; }
private:
    bool &block;
    bool reset;
};

void Q_CORE_EXPORT qDeleteInEventHandler(QObject *o);

struct QAbstractDynamicMetaObject;
struct Q_CORE_EXPORT QDynamicMetaObjectData
{
    virtual ~QDynamicMetaObjectData();
    virtual void objectDestroyed(QObject *) { delete this; }

    virtual QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *) = 0;
    virtual int metaCall(QObject *, QMetaObject::Call, int _id, void **) = 0;
};

struct Q_CORE_EXPORT QAbstractDynamicMetaObject : public QDynamicMetaObjectData, public QMetaObject
{
    ~QAbstractDynamicMetaObject();

    QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *) override { return this; }
    virtual int createProperty(const char *, const char *) { return -1; }
    int metaCall(QObject *, QMetaObject::Call c, int _id, void **a) override
    { return metaCall(c, _id, a); }
    virtual int metaCall(QMetaObject::Call, int _id, void **) { return _id; } // Compat overload
};

QT_END_NAMESPACE

#endif // QOBJECT_P_H

代码量也不多,不过很多朋友对于看源码这件事情,刚开始看到这一坨代码可能内心莫名的就会生出反感的情绪来。这里说说博主的方法哈。
在这里插入图片描述
我们基于Qt的框架开发,特别是在刚接触的情况下,最有效和快速准确的熟悉Qt模块/class/API等的方法,肯定是查看帮助手册。而且我也不止一次的表扬过Qt的Assistant, 写的是真的好,条理清晰,一目了然。 只不过,我们现在调试源码,你在帮助手册中去查看QObjectPrivate,这定然是无法查找到的。但是,帮助手册中的目录却值得借鉴。

首先,明确我们当前目标为 QObject 的私有类QObjectPrivate,我们重点要关注以下几点

  • 1、QObjectPrivate 是什么,主要有什么作用?
  • 2、QObjectPrivate 的继承关系?(这个很重要,能帮你在后面快速厘清成员变量和成员函数,以及哪些是重写的函数,以及哪些是自己特有的函数)
  • 3、它包含哪些成员变量成员函数?大概起到了什么作用?

从这个角度来梳理,那我们就相对比较容易了。
让大家觉得不容易的,可能就是在查看源码的过程中包含了各种语法糖,各种看着就很高级的,不知如何下手。不过反过来思考,你暂时看着、理解着难受的,不都是你未知,并可以前进的方向么,这是一件很开心的事情啊。博主奉行的就是“终生学习”的理念,对于未知常常会充满兴趣,并决心去愉快的探索。

2、继承关系

QObjectPrivate -> QObjectData

继承的深度只有一层,舒服,简单搂一眼QObjectData

class Q_CORE_EXPORT QObjectData {
    Q_DISABLE_COPY(QObjectData)
public:
    QObjectData() = default;
    virtual ~QObjectData() = 0; // 这个接口表明了QObjectData是一个接口类,只能被继承
    QObject *q_ptr;				// 原来q指针刺客在这里申明的
    QObject *parent;			// 每个QObject的对象或者子类对象都有记住父类的对象指针,那么在查找某个对象的子类对象时就轻而易举了
    QObjectList children;   	// typedef QList<QObject*> QObjectList;  这里更直接,直接记录了该对象的所有子类对象;每个子类对象展开又包含有同样的结构,说说看这是什么,可不就是一个嵌套的链式结构么

    uint isWidget : 1;			// 位域的知识不多说
    uint blockSig : 1;
    uint wasDeleted : 1;
    uint isDeletingChildren : 1;
    uint sendChildEvents : 1;
    uint receiveChildEvents : 1;
    uint isWindow : 1; //for QWindow
    uint deleteLaterCalled : 1;
    uint unused : 24;
    
    int postedEvents;
    
    QDynamicMetaObjectData *metaObject;
    QMetaObject *dynamicMetaObject() const;

#ifdef QT_DEBUG
    enum { CheckForParentChildLoopsWarnDepth = 4096 };
#endif
};

看完了QObjectData 的数据结构,其实我们有一个很有趣的问题引出来。

我们都知道Qt中所有的对象都继承自QObject,譬如QWidget也是继承自QObject 的。我们在编码的过程中,为什么实例化QObject 的子类对象,我们只说在堆上为对象申请内存的情况,为何却不需要自己编码去显示调用对象内存释放函数,来销毁对象实例呢?

问题抛出来,我们会在循序渐进的过程中解答这个问题~ 当然,如果是已经看到这儿的朋友,你怀有强烈的好奇心,也不妨自己动动手去寻找答案(注释中其实已经给了提示了哈)

3、成员变量

从代码中找成员变量,有时候代码行数比较多,且成员变量的定义比较分散的时候,确实不宜直接查看。这时候我们得想想办法。
在最近的 《2024了,我不允许你还不会:Qt查看与调试源码》一文中,同大家分享了Qt源码调试环境的搭建技巧。这里我们也是写一段简单的代码,通过调试器来辅助我们查看源码:

调试代码:

#include <QCoreApplication>
#include <QObject>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QObject o1,o2;
    QObject::connect(&o1,&QObject::destroyed, &o2,&QObject::destroyed);

    return a.exec();
}

我们始终要调试的时QObject 对吧,我们也说了 QObject QObjectPrivate的关系,对吧。
好的,那我们如下打两个断点。然后开始愉快的探索之旅。

在这里插入图片描述

如上,我们实例化了两个QObject的对象,分别是o1o2;
在这里插入图片描述
我们看到,QObject的实例化对象o1中包含了4个成员变量,分别是vptrd_ptr 以及staticMetaObjectstaticQtMetaObject .

  • vptr 从何而来? 我们回归下C++的基础知识,QObject 是一个虚类,当实例化对象时,编译器会自动为实例化对象添加一个内置的成员变量 vptr(即我们常说的虚表指针,指向QObject 类在编译时生成的虚函数表)。
  • d_ptr 是啥? 我们在源码中很快可以找到其定义:QScopedPointer<QObjectData> d_ptr; 指向 QObjectData数据成员变量的智能指针。
  • staticQtMetaObject 是怎么定义的? static const QMetaObject staticQtMetaObject; 即元对象类型的静态常成员变量。
  • staticMetaObject 呢?同上。

我们回到本章主题,在来看看 QObjectPrivate.
前面我们已经说过,QObjectPrivate 公有继承自 QObjectData,在QObject类中定义了QScopedPointer<QObjectData> d_ptr;
QObject的构造函数里 ,定义了d_ptr;

QObject::QObject(QObject *parent)
    : d_ptr(new QObjectPrivate)
{
  // ...
}

所以,这里就是我们常说的“向上造型”,用基类(QObjectData)指针 指向子类(QObjectPrivate)对象.

我们再借助调试器来看看 QObjectPrivate实例化对象的布局:
在这里插入图片描述
因为 QObjectPrivate 共有继承自QObjectData,所以,它可以访问 QObjectData的所有成员变量(QObjectData 中所有成员变量都是 public属性的)。
其次,它还含有如下几个成员变量:

  • currentChildBeingDeleted :
  • declarativeData
    这俩哥们被定义在一个联合体中,即两个存在互斥关系。
union {
   QObject *currentChildBeingDeleted; // should only be used when QObjectData::isDeletingChildren is set
   QAbstractDeclarativeData *declarativeData; //extra data used by the declarative module
};
  • extraData : extra data set by the user
  • shareRefcount : these objects are all used to indicate that a QObject was deleted plus QPointer, which keeps a separate list
QAtomicPointer<QtSharedPointer::ExternalRefCountData> sharedRefcount;
  • threadData : id of the thread that owns the object

  • connections : 记录信号和槽的连接信息

QAtomicPointer<ConnectionData> connections;

我特意把 connections 放到下面来说,因为这里非常有意思。继续往下挖,就到了Qt信号和槽机制的了,黑大粗那种。看看 ConnectionData 类:

struct ConnectionData {
        // the id below is used to avoid activating new connections. When the object gets
        // deleted it's set to 0, so that signal emission stops
        QAtomicInteger<uint> currentConnectionId;
        
        QAtomicInt ref;  
        
        QAtomicPointer<SignalVector> signalVector;  /// 信号容器
        Connection *senders = nullptr;
        Sender *currentSender = nullptr;   // object currently activating the object
        QAtomicPointer<Connection> orphaned;

        ~ConnectionData()
        {
            deleteOrphaned(orphaned.loadRelaxed());
            SignalVector *v = signalVector.loadRelaxed();
            if (v)
                free(v);
        }

        // must be called on the senders connection data
        // assumes the senders and receivers lock are held
        void removeConnection(Connection *c);
        void cleanOrphanedConnections(QObject *sender)
        {
            if (orphaned.loadRelaxed() && ref.loadAcquire() == 1)
                cleanOrphanedConnectionsImpl(sender);
        }
        void cleanOrphanedConnectionsImpl(QObject *sender);

        ConnectionList &connectionsForSignal(int signal)
        {
            return signalVector.loadRelaxed()->at(signal);
        }

        void resizeSignalVector(uint size) {
            SignalVector *vector = this->signalVector.loadRelaxed();
            if (vector && vector->allocated > size)
                return;
            size = (size + 7) & ~7;
            SignalVector *newVector = reinterpret_cast<SignalVector *>(malloc(sizeof(SignalVector) + (size + 1) * sizeof(ConnectionList)));
            int start = -1;
            if (vector) {
                memcpy(newVector, vector, sizeof(SignalVector) + (vector->allocated + 1) * sizeof(ConnectionList));
                start = vector->count();
            }
            for (int i = start; i < int(size); ++i)
                newVector->at(i) = ConnectionList();
            newVector->next = nullptr;
            newVector->allocated = size;

            signalVector.storeRelaxed(newVector);
            if (vector) {
                vector->nextInOrphanList = orphaned.loadRelaxed();
                orphaned.storeRelaxed(ConnectionOrSignalVector::fromSignalVector(vector));
            }
        }
        int signalVectorCount() const {
            return  signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1;
        }

        static void deleteOrphaned(ConnectionOrSignalVector *c);
};
struct ConnectionOrSignalVector {
        union {
            // linked list of orphaned connections that need cleaning up
            // 需要清理的孤立连接的链接列表
            ConnectionOrSignalVector *nextInOrphanList;
            // linked list of connections connected to slots in this object
            // 连接到此对象中插槽的连接的链接列表
            Connection *next;
        };

        static SignalVector *asSignalVector(ConnectionOrSignalVector *c) {
            if (reinterpret_cast<quintptr>(c) & 1)
                return reinterpret_cast<SignalVector *>(reinterpret_cast<quintptr>(c) & ~quintptr(1u));
            return nullptr;
        }
        static Connection *fromSignalVector(SignalVector *v) {
            return reinterpret_cast<Connection *>(reinterpret_cast<quintptr>(v) | quintptr(1u));
        }
    };



struct SignalVector : public ConnectionOrSignalVector {
        quintptr allocated;
        // ConnectionList signals[]
        ConnectionList &at(int i)
        {
            return reinterpret_cast<ConnectionList *>(this + 1)[i + 1];
        }
        const ConnectionList &at(int i) const
        {
            return reinterpret_cast<const ConnectionList *>(this + 1)[i + 1];
        }
        int count() const { return static_cast<int>(allocated); }
};
 // ConnectionList is a singly-linked list
    struct ConnectionList {
        QAtomicPointer<Connection> first;
        QAtomicPointer<Connection> last;
    };

ConnectionList 是一个单链表;每个节点都是一个Connection 对象。

 struct Connection : public ConnectionOrSignalVector
 {
        // linked list of connections connected to slots in this object, next is in base class
        Connection **prev;
        // linked list of connections connected to signals in this object
        QAtomicPointer<Connection> nextConnectionList;
        Connection *prevConnectionList;

        QObject *sender;    // 发送者对象
        QAtomicPointer<QObject> receiver; // 接收者对象
        QAtomicPointer<QThreadData> receiverThreadData; // 接收者线程id
	
		/**
		 typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);

		  // internal base class (interface) containing functions required to call a slot managed by a pointer to function.
    class QSlotObjectBase {
        QAtomicInt m_ref;
        // don't use virtual functions here; we don't want the
        // compiler to create tons of per-polymorphic-class stuff that
        // we'll never need. We just use one function pointer.
        typedef void (*ImplFn)(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret);
        const ImplFn m_impl;
    protected:
        enum Operation {
            Destroy,
            Call,
            Compare,

            NumOperations
        };
    public:
        explicit QSlotObjectBase(ImplFn fn) : m_ref(1), m_impl(fn) {}

        inline int ref() noexcept { return m_ref.ref(); }
        inline void destroyIfLastRef() noexcept
        { if (!m_ref.deref()) m_impl(Destroy, this, nullptr, nullptr, nullptr); }

        inline bool compare(void **a) { bool ret = false; m_impl(Compare, this, nullptr, a, &ret); return ret; }
        inline void call(QObject *r, void **a)  { m_impl(Call,    this, r, a, nullptr); }
    protected:
        ~QSlotObjectBase() {}
    private:
        Q_DISABLE_COPY_MOVE(QSlotObjectBase)
    };

	*/

		/// 定义了连接绑定的槽函数
        union {
            StaticMetaCallFunction callFunction;
            QtPrivate::QSlotObjectBase *slotObj;
        };
        QAtomicPointer<const int> argumentTypes;
        QAtomicInt ref_;
        uint id = 0;
        
        
        ushort method_offset;
        ushort method_relative;
        
        int signal_index : 27; // In signal range (see QObjectPrivate::signalIndex())
        
        ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
        ushort isSlotObject : 1;
        ushort ownArgumentTypes : 1;
        Connection() : ref_(2), ownArgumentTypes(true) {
            //ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection
        }
        ~Connection();
        
        int method() const { 
        	Q_ASSERT(!isSlotObject); 
        	return method_offset + method_relative; 
        }
        void ref() { ref_.ref(); }
        void freeSlotObject()
        {
            if (isSlotObject) {
                slotObj->destroyIfLastRef();
                isSlotObject = false;
            }
        }
        void deref() {
            if (!ref_.deref()) {
                Q_ASSERT(!receiver.loadRelaxed());
                Q_ASSERT(!isSlotObject);
                delete this;
            }
        }
};

Connection 对象中定义了对象连接的信号和槽:

  • 一个对象既可以定义成信号的发送者,也可以定义成信号的接收者
  • 一个对象可以接收多个信号
  • 一个发送者对象可以绑定多个接收者信号及对应的槽函数

我们再回头看两个函数:

// Used by QAccessibleWidget
QObjectList QObjectPrivate::receiverList(const char *signal) const
{
    QObjectList returnValue;
    int signal_index = signalIndex(signal);
    ConnectionData *cd = connections.loadRelaxed();
    if (signal_index < 0 || !cd)
        return returnValue;
    if (signal_index < cd->signalVectorCount()) {
        const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();

        while (c) {
            QObject *r = c->receiver.loadRelaxed();
            if (r)
                returnValue << r;
            c = c->nextConnectionList.loadRelaxed();
        }
    }
    return returnValue;
}

// Used by QAccessibleWidget
QObjectList QObjectPrivate::senderList() const
{
    QObjectList returnValue;
    ConnectionData *cd = connections.loadRelaxed();
    if (cd) {
        QBasicMutexLocker locker(signalSlotLock(q_func()));
        for (Connection *c = cd->senders; c; c = c->next)
            returnValue << c->sender;
    }
    return returnValue;
}

看到这里,我们对于QObject 中对象数据类 QObjectPrivate 有了一些认识了。

楼已高,起下篇~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1419835.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

宝塔控制面板配置SSL证书实现网站HTTPS

宝塔安装SSL证书提前申请好SSL证书&#xff0c;如果还没有&#xff0c;先去Gworg里面申请&#xff0c;一般几分钟就可以下来&#xff0c;申请地址&#xff1a;首页-Gworg官方店-淘宝网 一、登录邮箱下载&#xff1a;Gworg证书文件目录 &#xff0c;都会有以下五个文件夹。宝塔…

红外热成像仪定制_热成像仪/红外夜视仪开发方案

红外热成像技术是一种利用红外热成像仪将物体发出的不可见红外辐射能量转换成可见的温度场图像的技术&#xff0c;通过不同颜色来表示不同温度。这项技术的应用领域非常广泛&#xff0c;从电路维修到暖通检测再到汽车故障排查等各个领域都有着重要的作用。 红外热成像仪的解决方…

数字人解决方案VividTalk——音频驱动单张照片实现人物头像说话的效果

前言 VividTalk是一项由南京大学、阿里巴巴、字节跳动和南开大学共同开发的创新项目。该项目通过结合单张人物静态照片和一段语音录音&#xff0c;能够制作出一个看起来仿佛实际说话的人物视频。项目的特点包括自然的面部表情和头部动作&#xff0c;口型能够同步&#xff0c;同…

搞明白手机卡的合约期和优惠期才能避免很多坑!

很多朋友注销流量卡时才发现自己的套餐有合约期无法注销&#xff0c;尤其是联通和移动&#xff0c;那么什么是合约期呢&#xff1f;合约期和优惠期又有什么不一样呢&#xff1f;下来答案来了。 其实&#xff0c;目前很多在网上办理的大流量卡都是有合约期的&#xff0c;尤其是移…

05. 交换机的基本配置

文章目录 一. 初识交换机1.1. 交换机的概述1.2. Ethernet_ll格式1.3. MAC分类1.4. 冲突域1.5. 广播域1.6. 交换机的原理1.7. 交换机的3种转发行为 二. 初识ARP2.1. ARP概述2.2. ARP报文格式2.3. ARP的分类2.4. 免费ARP的作用 三. 实验专题3.1. 实验1&#xff1a;交换机的基本原…

JSP仓储管理系统myeclipse定制开发SQLServer数据库网页模式java编程jdbc

一、源码特点 JSP仓储管理系统系统是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库 &#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为SQLServer2008&#x…

小白水平理解面试经典题目LeetCode 455 Assign Cookies【Java实现】

455 分配cookies 小白渣翻译&#xff1a; 假设你是一位很棒的父母&#xff0c;想给你的孩子一些饼干。但是&#xff0c;你最多应该给每个孩子一块饼干。 每个孩子 i 都有一个贪婪因子 g[i] &#xff0c;这是孩子满意的 cookie 的最小大小&#xff1b;每个 cookie j 都有一个…

非内积级联学习

1.首页推荐非内积召回现状 非内积召回源是目前首页推荐最重要的召回源之一。同时非内积相比于向量化召回最终仅将user和item匹配程度表征为embeding内积&#xff0c;非内积召回仅保留item embedding&#xff0c;不构造user显式表征&#xff0c;而是通过一个打分网络计算用户-商…

142. 环形链表 II(力扣LeetCode)

文章目录 142. 环形链表 II题目描述解题思路判断链表是否有环如果有环&#xff0c;如何找到这个环的入口 c代码 142. 环形链表 II 题目描述 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个…

thinkadmin操作栏审核通过(操作确认),审核驳回(录入信息)

录入信息页面 {extend name="../../admin/view/main"}{block name=content} <style>textarea {font-size: 16px;padding: 10px;border: 1px solid #ccc;

格子表单GRID-FORM | 嵌套子表单与自定义脚本交互

格子表单/GRID-FORM已在Github 开源&#xff0c;如能帮到您麻烦给个星&#x1f91d; GRID-FORM 系列文章 基于 VUE3 可视化低代码表单设计器嵌套表单与自定义脚本交互 新版本功能 &#x1f389; 不觉间&#xff0c;GRID-FORM 已经开源一年&#xff08;2023年1月29日首次提交…

蓝桥杯---加法变乘法

我们都知道:123 ….. 491225&#xff0c;现在要求你把其中两个不相邻的加号变成乘号,使得结果为2015 比如&#xff1a;123 ... 10*1112 ... 27*2829 ... 492015 就是符合要求的答案. 请你寻找另外一个可能的答案,并把位置靠前的那个乘号左边的数字提交(对于示例,就是 提交10)…

inode生命周期

1.添加inode到inode cache链表 当inode的引用计数器i_count为0后&#xff0c;会调用iput_final去释放 static void iput_final(struct inode *inode) {struct super_block *sb inode->i_sb;const struct super_operations *op inode->i_sb->s_op;unsigned long sta…

07.领域驱动设计:了解3种常见微服务架构模型的对比和分析

目录 1、概述 2、整洁架构 3、六边形架构 4、三种微服务架构模型的对比和分析 5、从三种架构模型看中台和微服务设计 5.1 中台建设要聚焦领域模型 5.2 微服务要有合理的架构分层 5.2.1 项目级微服务 5.2.2 企业级中台微服务 5.3 应用和资源的解耦与适配 6、总结 1、概…

云计算底层技术、磁盘技术揭秘虚拟化管理、公有云概述

查看本机是否具备虚拟化支持 硬件辅助虚拟化 处理器里打开 虚拟化Inter VT-x/EPT 或AMD-V 构建虚拟化平台工具软件包 yum 与 dnf Yum和DNF都是用于管理Linux系统中的软件包的工具&#xff0c;但它们在许多方面存在一些差异。以下是一些可能的区别&#xff1a; 依赖解…

Qt|QPushButton控件讲解

前提 按钮分为了四种状态&#xff1a;常态、聚焦、按下、禁用 前一段时间更新了MFC框架下CButton的自绘。因为MFC框架下的按钮限制性很高&#xff0c;所以只能由自绘实现各种风格&#xff0c;但是QT框架完美的解决了这个问题&#xff0c;我们只需要了解如何调用&#xff0c;就…

Doris简介及单机部署(超详细)

文章目录 一、Doris简介1、Doris介绍2、Doris架构 二、Doris单机部署&#xff08;Centos7.9&#xff09;1、下载Doris2、准备环境3、安装部署3.1 创建存储目录3.2 配置 FE3.3 启动 FE3.4 查看 FE 运行状态3.5 配置 BE3.6 启动 BE3.7 添加 BE 节点到集群3.8 查看 BE 运行状态3.9…

elementui中的tree自定义图标

需求&#xff1a;实现如下样式的树形列表 自定义树的图标以及点击时&#xff0c;可以根据子级的关闭&#xff0c;切换图标 <el-tree :data"treeList" :props"defaultProps"><template #default"{ node, data }"><span class&quo…

C++PythonC# 三语言OpenCV从零开发(7):图像的阈值

文章目录 相关链接前言阈值阈值使用代码PythonCCsharpcsharp代码问题 总结 相关链接 C&Python&Csharp in OpenCV 专栏 【2022B站最好的OpenCV课程推荐】OpenCV从入门到实战 全套课程&#xff08;附带课程课件资料课件笔记&#xff09; OpenCV一个窗口同时显示多张图片 …

C++提取ICO图标(PE文件资源提取)

最近需要写一个提取EXE或者DLL图标资源的功能, 网上找了很久, 要么功能不好用, 最后结果如下: 1.很多是加载为HICON句柄后转换为图片保存, 全损画质..., 2.后来找了个还能用的, 详见 https://github.com/TortoiseGit/TortoiseGit/blob/master/src/Utils/IconExtractor.cpp …