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

2024-01-29 19:04

本文主要是介绍温酒读Qt:QObject中篇2 ——欲遮还羞的 QObjectPrivate,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


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


传送门:
《温酒读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 &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 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的对象,分别是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 setQAbstractDeclarativeData *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 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 有了一些认识了。

楼已高,起下篇~

这篇关于温酒读Qt:QObject中篇2 ——欲遮还羞的 QObjectPrivate的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/657882

相关文章

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

【QT】基础入门学习

文章目录 浅析Qt应用程序的主函数使用qDebug()函数常用快捷键Qt 编码风格信号槽连接模型实现方案 信号和槽的工作机制Qt对象树机制 浅析Qt应用程序的主函数 #include "mywindow.h"#include <QApplication>// 程序的入口int main(int argc, char *argv[]){// argc是命令行参数个数,argv是

Python QT实现A-star寻路算法

目录 1、界面使用方法 2、注意事项 3、补充说明 用Qt5搭建一个图形化测试寻路算法的测试环境。 1、界面使用方法 设定起点: 鼠标左键双击,设定红色的起点。左键双击设定起点,用红色标记。 设定终点: 鼠标右键双击,设定蓝色的终点。右键双击设定终点,用蓝色标记。 设置障碍点: 鼠标左键或者右键按着不放,拖动可以设置黑色的障碍点。按住左键或右键并拖动,设置一系列黑色障碍点

使用Qt编程QtNetwork无法使用

使用 VS 构建 Qt 项目时 QtNetwork 无法使用的问题 - 摘叶飞镖 - 博客园 (cnblogs.com) 另外,强烈建议在使用QNetworkAccessManager之前看看这篇文章: Qt 之 QNetworkAccessManager踏坑记录-CSDN博客 C++ Qt开发:QNetworkAccessManager网络接口组件 阅读目录 1.1 通用API函数

Qt多语种开发教程

Qt作为跨平台的开发工具,早已应用到各行各业的软件开发中。 今天讲讲,Qt开发的正序怎么做多语言开发。就是说,你设置中文,就中文显示;设置英语就英文显示,设置繁体就繁体显示,设置发育就显示法语等。 开发环境(其实多语种这块根环境没太大关系):win10,Qt.5.12.10 一.先用QtCreator创建一个简单的桌面程序 1.工程就随便命名“LanguageTest”,其他默认。 2.在设计师

Qt中window frame的影响

window frame 在创建图形化界面的时候,会创建窗口主体,上面会多出一条,周围多次一圈细边,这就叫window frame窗口框架,这是操作系统自带的。 这个对geometry的一些属性有一定影响,主要体现在Qt坐标系体系: 窗口当中包含一个按钮,这个按钮的坐标系是以父元素为参考,那么这个参考是widget本体作为参考,还是window frame作为参考,这两种参考体系都存在

【Qt】定时器事件

定时器事件 在之前学习QTimer中实现了定时器的功能,而在QTimer背后是QTimerEvent定时器事件进行支撑的。在QObject中提供了一个timeEvent这个函数。 startTimer启动定时器killTimer关闭定时器 Qt 中在进⾏窗⼝程序的处理过程中,经常要周期性的执⾏某些操作,或者制作⼀些动画效果,使⽤定 时器就可以实现。所谓定时器就是在间隔⼀定时间后,去执⾏某⼀

QT 编译报错:C3861: ‘tr‘ identifier not found

问题: QT 编译报错:C3861: ‘tr’ identifier not found 原因 使用tr的地方所在的类没有继承自 QObject 类 或者在不在某一类中, 解决方案 就直接用类名引用 :QObject::tr( )

在 Qt Creator 中,输入 /** 并按下Enter可以自动生成 Doxygen 风格的注释

在 Qt Creator 中,当你输入 /** 时,确实会自动补全标准的 Doxygen 风格注释。这是因为 Qt Creator 支持 Doxygen 以及类似的文档注释风格,并且提供了代码自动补全功能。 以下是如何在 Qt Creator 中使用和显示这些注释标记的步骤: 1. 自动补全 Doxygen 风格注释 在 Qt Creator 中,你可以这样操作: 在你的代码中,将光标放在

【Python篇】PyQt5 超详细教程——由入门到精通(中篇二)

文章目录 PyQt5超详细教程前言第7部分:生成图表与数据可视化7.1 matplotlib 与 PyQt5 的结合7.2 在 PyQt5 中嵌入 matplotlib 图表示例 1:嵌入简单的 matplotlib 图表代码详解: 7.3 动态生成图表示例 2:动态更新图表代码详解: 7.4 在应用程序中展示不同类型的图表示例 3:展示不同类型的图表代码详解: 7.5 总结 第8部分:对话