《妙法莲华经》曰:“佛道长远,久受勤苦,乃可得成。” 世事修炼,莫不如是,日拱一卒无有尽,功不唐捐终入海。
传送门:
《温酒读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_NAMESPACEclass 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_USERDATAQVector<QObjectUserData *> userData;#endifQList<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 upConnectionOrSignalVector *nextInOrphanList;// linked list of connections connected to slots in this objectConnection *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 classConnection **prev;// linked list of connections connected to signals in this objectQAtomicPointer<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 == blockingushort 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 liststruct 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 correspondto the signal index. The signal index is the one returned by QObjectPrivate::signalIndex (notQMetaObject::indexOfSignal). allsignals contains a list of special connections that will get invoked onany 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 connectedto a slot in this object. The mutex of the receiver must be locked when touching the pointers of thislinked 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 stopsQAtomicInteger<uint> currentConnectionId;QAtomicInt ref;QAtomicPointer<SignalVector> signalVector;Connection *senders = nullptr;Sender *currentSender = nullptr; // object currently activating the objectQAtomicPointer<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 heldvoid 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 userQThreadData *threadData; // id of the thread that owns the objectusing ConnectionDataPointer = QExplicitlySharedDataPointer<ConnectionData>;QAtomicPointer<ConnectionData> connections;union {QObject *currentChildBeingDeleted; // should only be used when QObjectData::isDeletingChildren is setQAbstractDeclarativeData *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 listQAtomicPointer<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 QtPrivatetemplate <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 callerQMetaCallEvent(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 callerQMetaCallEvent(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 argumentschar 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 █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 QWindowuint deleteLaterCalled : 1;uint unused : 24;int postedEvents;QDynamicMetaObjectData *metaObject;QMetaObject *dynamicMetaObject() const;#ifdef QT_DEBUGenum { 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
的对象,分别是o1
和o2
;
我们看到,QObject
的实例化对象o1
中包含了4个成员变量,分别是vptr
、d_ptr
以及staticMetaObject
和staticQtMetaObject
.
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 setQAbstractDeclarativeData *declarativeData; //extra data used by the declarative module
};
extraData
: extra data set by the usershareRefcount
: 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 stopsQAtomicInteger<uint> currentConnectionId;QAtomicInt ref; QAtomicPointer<SignalVector> signalVector; /// 信号容器Connection *senders = nullptr;Sender *currentSender = nullptr; // object currently activating the objectQAtomicPointer<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 heldvoid 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 liststruct 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 classConnection **prev;// linked list of connections connected to signals in this objectQAtomicPointer<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 == blockingushort 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
有了一些认识了。
楼已高,起下篇~