QObject
上一节中我们讲了QObject是Qt中使用Meta-Object元对象模型或者说使用信号与槽机制,必须继承的根基类,一般面向对象语言都会有这么一个根基类,提供了语言的基础,那么Qt作为C++的扩展库,QObject作为Qt的根类,为我们提供了哪些功能呢?
对象树
在Qt的构造函数中,我们可以发现都带有一个QObject* parent=0的默认参数,这个parent就是用来指定父对象
QObject::QObject(QObject *parent)
: d_ptr(new QObjectPrivate)
{
Q_D(QObject);
d_ptr->q_ptr = this;
d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current();
d->threadData->ref();
if (parent) {
QT_TRY {
if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData))
parent = 0;
setParent(parent);
} QT_CATCH(...) {
d->threadData->deref();
QT_RETHROW;
}
}
qt_addObject(this);
if (Q_UNLIKELY(qtHookData[QHooks::AddQObject]))
reinterpret_cast<QHooks::AddQObjectCallback>(qtHookData[QHooks::AddQObject])(this);
}
先看看d_ptr的定义
QScopedPointer<QObjectData> d_ptr;
它是一个QObjectData的指针,在Qt源码中一般用d_ptr表示类的数据指针。
class Q_CORE_EXPORT QObjectData {
public:
virtual ~QObjectData() = 0;
QObject *q_ptr;
QObject *parent;
QObjectList children;
uint isWidget : 1;
uint blockSig : 1;
uint wasDeleted : 1;
uint isDeletingChildren : 1;
uint sendChildEvents : 1;
uint receiveChildEvents : 1;
uint isWindow : 1; //for QWindow
uint unused : 25;
int postedEvents;
QDynamicMetaObjectData *metaObject;
QMetaObject *dynamicMetaObject() const;
};
QObjectData 定义了一个q_ptr指针指回QObject,parent 指向父对象,children保存子对象,还定义了一些标志位,当然还有我们上节的QMetaObject 类元信息。
在构造函数有这么一句
d_ptr(new QObjectPrivate)
那么QObjectPrivate是什么,既然它能赋值给d_ptr,那应该是QObjectData的派生类,转到定义看看。
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;
};
//...
}
果然,它就是继承自QObjectData,然后带了些额外的数据
#define Q_DECLARE_PUBLIC(Class) \
inline Class* q_func() { return static_cast<Class *>(q_ptr); } \
inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \
friend class Class;
Q_DECLARE_PUBLIC宏声明了友元类,并通过q_func()方法返回q_ptr,即QObject类指针
在QObject的声明中也有如下一句:
Q_DECLARE_PRIVATE(QObject)
看看Q_DECLARE_PRIVATE的定义
#define Q_DECLARE_PRIVATE(Class) \
inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \
inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \
friend class Class##Private;
在Qt的源码中到处可以见到这样的好基友,通过声明一个名字为ClassPrivate的友元类,用来保存私有数据,保证数据的封装性和隐秘性
继续看QObject的构造函数,又有这么一个宏Q_D,Qt还真是喜欢用宏定义,这样可以使代码简洁些
#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()
就是定义一个d指向私有类,定义一个q指向本身。继续
d_ptr->q_ptr = this;
d_ptr通过new QObjectPrivate,指向QObjectPrivate,那么d_ptr->q_ptr = this就是将QObjectPrivate中的q_ptr指回QObject
不要被绕晕了,其实很简单,一句话总结
QObject和QObjectPrivate互为友元类,QObject中new了一个QObjectPrivate,通过d_ptr保存QObjectPrivate的指针,而QObjectPrivate中通过q_ptr指回QObejct,真是一对好基友
继续
d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current();
d->threadData->ref();
if (parent) {
QT_TRY {
if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData))
parent = 0;
setParent(parent);
} QT_CATCH(...) {
d->threadData->deref();
QT_RETHROW;
}
}
线程数据我们这里先不深究,大抵意思是比较父对象和当前对象是否是同一线程中创建,如果不是是会抛出异常的。这就是说Qt要求父对象和子对象必须在同一线程中创建,这是因为父对象后面既然负责子对象的销毁工作,如果是跨线程销毁,会带来毁灭性的灾害。
setParent(parent);就是赋值QObjectData中parent指针了,转到定义看最终调用如下
void QObjectPrivate::setParent_helper(QObject *o)
{
Q_Q(QObject);
if (o == parent)
return;
if (parent) {
QObjectPrivate *parentD = parent->d_func();
if (parentD->isDeletingChildren && wasDeleted
&& parentD->currentChildBeingDeleted == q) {
// don't do anything since QObjectPrivate::deleteChildren() already
// cleared our entry in parentD->children.
} else {
const int index = parentD->children.indexOf(q);
if (parentD->isDeletingChildren) {
parentD->children[index] = 0;
} else {
parentD->children.removeAt(index);
if (sendChildEvents && parentD->receiveChildEvents) {
QChildEvent e(QEvent::ChildRemoved, q);
QCoreApplication::sendEvent(parent, &e);
}
}
}
}
parent = o;
if (parent) {
// object hierarchies are constrained to a single thread
if (threadData != parent->d_func()->threadData) {
qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread");
parent = 0;
return;
}
parent->d_func()->children.append(q);
if(sendChildEvents && parent->d_func()->receiveChildEvents) {
if (!isWidget) {
QChildEvent e(QEvent::ChildAdded, q);
QCoreApplication::sendEvent(parent, &e);
}
}
}
if (!wasDeleted && !isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged)
QAbstractDeclarativeData::parentChanged(declarativeData, q, o);
}
一来赋值了parent,二来在parent的QObejectData中增加上该children,并且QCoreApplication::sendEvent给父对象发送了一个QChildEvent添加子类事件
继续是qt_addObject
// ### Qt >= 5.6, remove qt_add/removeObject
extern "C" Q_CORE_EXPORT void qt_addObject(QObject *)
{}
extern "C" Q_CORE_EXPORT void qt_removeObject(QObject *)
{}
从定义和注释看,qt_addObject和qt_removeObject这两个函数只是一个空壳了,应该是以前版本的遗留物。
if (Q_UNLIKELY(qtHookData[QHooks::AddQObject]))
reinterpret_cast<QHooks::AddQObjectCallback>(qtHookData[QHooks::AddQObject])(this);
qtHookData是Qt预留的钩子回调函数指针数组,当new一个QObject对象时,会触发QHooks::AddQObject钩子回调函数
再看看QObject的析构函数
QObject::~QObject()
{
Q_D(QObject);
d->wasDeleted = true;
d->blockSig = 0; // unblock signals so we always emit destroyed()
QtSharedPointer::ExternalRefCountData *sharedRefcount = d->sharedRefcount.load();
if (sharedRefcount) {
if (sharedRefcount->strongref.load() > 0) {
qWarning("QObject: shared QObject was deleted directly. The program is malformed and may crash.");
// but continue deleting, it's too late to stop anyway
}
// indicate to all QWeakPointers that this QObject has now been deleted
sharedRefcount->strongref.store(0);
if (!sharedRefcount->weakref.deref())
delete sharedRefcount;
}
if (!d->isWidget && d->isSignalConnected(0)) {
emit destroyed(this);
}
if (d->declarativeData) {
if (static_cast<QAbstractDeclarativeDataImpl*>(d->declarativeData)->ownedByQml1) {
if (QAbstractDeclarativeData::destroyed_qml1)
QAbstractDeclarativeData::destroyed_qml1(d->declarativeData, this);
} else {
if (QAbstractDeclarativeData::destroyed)
QAbstractDeclarativeData::destroyed(d->declarativeData, this);
}
}
// set ref to zero to indicate that this object has been deleted
if (d->currentSender != 0)
d->currentSender->ref = 0;
d->currentSender = 0;
if (d->connectionLists || d->senders) {
QMutex *signalSlotMutex = signalSlotLock(this);
QMutexLocker locker(signalSlotMutex);
// disconnect all receivers
if (d->connectionLists) {
++d->connectionLists->inUse;
int connectionListsCount = d->connectionLists->count();
for (int signal = -1; signal < connectionListsCount; ++signal) {
QObjectPrivate::ConnectionList &connectionList =
(*d->connectionLists)[signal];
while (QObjectPrivate::Connection *c = connectionList.first) {
if (!c->receiver) {
connectionList.first = c->nextConnectionList;
c->deref();
continue;
}
QMutex *m = signalSlotLock(c->receiver);
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
if (c->receiver) {
*c->prev = c->next;
if (c->next) c->next->prev = c->prev;
}
c->receiver = 0;
if (needToUnlock)
m->unlock();
connectionList.first = c->nextConnectionList;
// The destroy operation must happen outside the lock
if (c->isSlotObject) {
c->isSlotObject = false;
locker.unlock();
c->slotObj->destroyIfLastRef();
locker.relock();
}
c->deref();
}
}
if (!--d->connectionLists->inUse) {
delete d->connectionLists;
} else {
d->connectionLists->orphaned = true;
}
d->connectionLists = 0;
}
/* Disconnect all senders:
* This loop basically just does
* for (node = d->senders; node; node = node->next) { ... }
*
* We need to temporarily unlock the receiver mutex to destroy the functors or to lock the
* sender's mutex. And when the mutex is released, node->next might be destroyed by another
* thread. That's why we set node->prev to &node, that way, if node is destroyed, node will
* be updated.
*/
QObjectPrivate::Connection *node = d->senders;
while (node) {
QObject *sender = node->sender;
// Send disconnectNotify before removing the connection from sender's connection list.
// This ensures any eventual destructor of sender will block on getting receiver's lock
// and not finish until we release it.
sender->disconnectNotify(QMetaObjectPrivate::signal(sender->metaObject(), node->signal_index));
QMutex *m = signalSlotLock(sender);
node->prev = &node;
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
//the node has maybe been removed while the mutex was unlocked in relock?
if (!node || node->sender != sender) {
// We hold the wrong mutex
Q_ASSERT(needToUnlock);
m->unlock();
continue;
}
node->receiver = 0;
QObjectConnectionListVector *senderLists = sender->d_func()->connectionLists;
if (senderLists)
senderLists->dirty = true;
QtPrivate::QSlotObjectBase *slotObj = Q_NULLPTR;
if (node->isSlotObject) {
slotObj = node->slotObj;
node->isSlotObject = false;
}
node = node->next;
if (needToUnlock)
m->unlock();
if (slotObj) {
if (node)
node->prev = &node;
locker.unlock();
slotObj->destroyIfLastRef();
locker.relock();
}
}
}
if (!d->children.isEmpty())
d->deleteChildren();
qt_removeObject(this);
if (Q_UNLIKELY(qtHookData[QHooks::RemoveQObject]))
reinterpret_cast<QHooks::RemoveQObjectCallback>(qtHookData[QHooks::RemoveQObject])(this);
if (d->parent) // remove it from parent object
d->setParent_helper(0);
}
比较长,关键几处有:
emit destroyed(this);发出销毁信号
int connectionListsCount = d->connectionLists->count();
for (int signal = -1; signal < connectionListsCount; ++signal) {
QObjectPrivate::ConnectionList &connectionList =
(*d->connectionLists)[signal];
while (QObjectPrivate::Connection *c = connectionList.first) {
if (!c->receiver) {
connectionList.first = c->nextConnectionList;
c->deref();
continue;
}
//...
}
断开所有信号与槽的连接
if (!d->children.isEmpty())
d->deleteChildren();
删除子对象
if (Q_UNLIKELY(qtHookData[QHooks::RemoveQObject]))
reinterpret_cast<QHooks::RemoveQObjectCallback>(qtHookData[QHooks::RemoveQObject])(this);
触发移除对象的钩子回调函数
if (d->parent) // remove it from parent object
d->setParent_helper(0);
// setParent_helper中会执行如下语句
QChildEvent e(QEvent::ChildRemoved, q);
QCoreApplication::sendEvent(parent, &e);
从父对象的子对象列表中移除该对象,此时会向父对象发出一个移除子对象的事件
总结:在Qt的构造的析构函数中,就实现了对象树。每个对象会保留父对象指针和子对象列表,构造时设置父指针,并在父对象的子对象列表中添加该对象,析构时会删除所有子对象,并从父对象的子对象列表中移除该对象。
Qt的对象树很有用,在界面构造时,我们的子控件只需要指定了父控件,那么父控件销毁时会自动销毁子控件,而不用我们操心,所以在Qt的应用程序代码中你可以发现只有new,没有delete,这就是对象树提供的方便之处,妈妈再也不用担心内存泄漏问题了
信号与槽机制
信号与槽机制的根基通过QMetaObject提供,这在上一节已经讲了,通过signals,slots等宏声明定义信号、槽。QMetaObject中会记录这些信号与槽函数,并通过索引号查找,moc会自动添加上qt_metacall的实现,通过索引调用对应的函数。
这里主要看下connect和disconnect的实现。
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type)
{
if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
sender ? sender->metaObject()->className() : "(null)",
(signal && *signal) ? signal+1 : "(null)",
receiver ? receiver->metaObject()->className() : "(null)",
(method && *method) ? method+1 : "(null)");
return QMetaObject::Connection(0);
}
QByteArray tmp_signal_name;
if (!check_signal_macro(sender, signal, "connect", "bind"))
return QMetaObject::Connection(0);
const QMetaObject *smeta = sender->metaObject();
const char *signal_arg = signal;
++signal; //skip code
QArgumentTypeArray signalTypes;
Q_ASSERT(QMetaObjectPrivate::get(smeta)->revision >= 7);
QByteArray signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
int signal_index = QMetaObjectPrivate::indexOfSignalRelative(
&smeta, signalName, signalTypes.size(), signalTypes.constData());
if (signal_index < 0) {
// check for normalized signatures
tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
signal = tmp_signal_name.constData() + 1;
signalTypes.clear();
signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
smeta = sender->metaObject();
signal_index = QMetaObjectPrivate::indexOfSignalRelative(
&smeta, signalName, signalTypes.size(), signalTypes.constData());
}
if (signal_index < 0) {
err_method_notfound(sender, signal_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return QMetaObject::Connection(0);
}
signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
signal_index += QMetaObjectPrivate::signalOffset(smeta);
QByteArray tmp_method_name;
int membcode = extract_code(method);
if (!check_method_code(membcode, receiver, method, "connect"))
return QMetaObject::Connection(0);
const char *method_arg = method;
++method; // skip code
QArgumentTypeArray methodTypes;
QByteArray methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
const QMetaObject *rmeta = receiver->metaObject();
int method_index_relative = -1;
Q_ASSERT(QMetaObjectPrivate::get(rmeta)->revision >= 7);
switch (membcode) {
case QSLOT_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
&rmeta, methodName, methodTypes.size(), methodTypes.constData());
break;
case QSIGNAL_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
&rmeta, methodName, methodTypes.size(), methodTypes.constData());
break;
}
if (method_index_relative < 0) {
// check for normalized methods
tmp_method_name = QMetaObject::normalizedSignature(method);
method = tmp_method_name.constData();
methodTypes.clear();
methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
// rmeta may have been modified above
rmeta = receiver->metaObject();
switch (membcode) {
case QSLOT_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
&rmeta, methodName, methodTypes.size(), methodTypes.constData());
break;
case QSIGNAL_CODE:
method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
&rmeta, methodName, methodTypes.size(), methodTypes.constData());
break;
}
}
if (method_index_relative < 0) {
err_method_notfound(receiver, method_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return QMetaObject::Connection(0);
}
if (!QMetaObjectPrivate::checkConnectArgs(signalTypes.size(), signalTypes.constData(),
methodTypes.size(), methodTypes.constData())) {
qWarning("QObject::connect: Incompatible sender/receiver arguments"
"\n %s::%s --> %s::%s",
sender->metaObject()->className(), signal,
receiver->metaObject()->className(), method);
return QMetaObject::Connection(0);
}
int *types = 0;
if ((type == Qt::QueuedConnection)
&& !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size()))) {
return QMetaObject::Connection(0);
}
#ifndef QT_NO_DEBUG
QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
check_and_warn_compat(smeta, smethod, rmeta, rmethod);
#endif
QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(
sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));
return handle;
}
通过SIGNAL和SLOT宏传入的是函数签名字符串,所以会先调用QMetaObjectPrivate::decodeMethodSignature去解析函数签名,再调用QMetaObjectPrivate::indexOfSlotRelative去获取她们在QMetaObject中记录的索引
最终调用QMetaObjectPrivate::connect
继续跟踪
QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
int signal_index, const QMetaObject *smeta,
const QObject *receiver, int method_index,
const QMetaObject *rmeta, int type, int *types)
{
QObject *s = const_cast<QObject *>(sender);
QObject *r = const_cast<QObject *>(receiver);
int method_offset = rmeta ? rmeta->methodOffset() : 0;
Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6);
QObjectPrivate::StaticMetaCallFunction callFunction =
rmeta ? rmeta->d.static_metacall : 0;
QOrderedMutexLocker locker(signalSlotLock(sender),
signalSlotLock(receiver));
if (type & Qt::UniqueConnection) {
QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
if (connectionLists && connectionLists->count() > signal_index) {
const QObjectPrivate::Connection *c2 =
(*connectionLists)[signal_index].first;
int method_index_absolute = method_index + method_offset;
while (c2) {
if (!c2->isSlotObject && c2->receiver == receiver && c2->method() == method_index_absolute)
return 0;
c2 = c2->nextConnectionList;
}
}
type &= Qt::UniqueConnection - 1;
}
QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
c->sender = s;
c->signal_index = signal_index;
c->receiver = r;
c->method_relative = method_index;
c->method_offset = method_offset;
c->connectionType = type;
c->isSlotObject = false;
c->argumentTypes.store(types);
c->nextConnectionList = 0;
c->callFunction = callFunction;
QObjectPrivate::get(s)->addConnection(signal_index, c.data());
locker.unlock();
QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
if (smethod.isValid())
s->connectNotify(smethod);
return c.take();
}
应该明白了,new 一个QObjectPrivate::Connection,里面保存了发送者,信号索引,接受者,方法索引,连接类型等信息,再终调用QObjectPrivate::get(s)->addConnection(signal_index, c.data());
当然还调用了一个连接通知函数connectNotify
void QObjectPrivate::addConnection(int signal, Connection *c)
{
Q_ASSERT(c->sender == q_ptr);
if (!connectionLists)
connectionLists = new QObjectConnectionListVector();
if (signal >= connectionLists->count())
connectionLists->resize(signal + 1);
ConnectionList &connectionList = (*connectionLists)[signal];
if (connectionList.last) {
connectionList.last->nextConnectionList = c;
} else {
connectionList.first = c;
}
connectionList.last = c;
cleanConnectionLists();
c->prev = &(QObjectPrivate::get(c->receiver)->senders);
c->next = *c->prev;
*c->prev = c;
if (c->next)
c->next->prev = &c->next;
if (signal < 0) {
connectedSignals[0] = connectedSignals[1] = ~0;
} else if (signal < (int)sizeof(connectedSignals) * 8) {
connectedSignals[signal >> 5] |= (1 << (signal & 0x1f));
}
}
真相大白了,new了一个QObjectConnectionListVector连接列表向量,ConnectionList &connectionList = (*connectionLists)[signal];通过信号索引找到连接列表,也就是说一个信号可以连接多个接收,连接列表向量中保存所有信号的连接列表。
disconnect流程和connect大抵类似:
解析函数签名->提取对应索引->调用QMetaObjectPrivate::disconnect->从连接列表向量中提取对应信号的连接列表->从连接列表中将ref连接次数-1,如果为0则移出列表->调用断连通知函数disconnectNotify
属性系统
在Meta-Object Model中我们已经讲过Qt提供了动态的添加属性的方法,在QObject中给出的接口就是:
bool setProperty(const char *name, const QVariant &value);
QVariant property(const char *name) const;
QList<QByteArray> dynamicPropertyNames() const;
事件系统
QObject中提供了如下几个事件处理接口:
void installEventFilter(QObject *filterObj);
void removeEventFilter(QObject *obj);
virtual bool event(QEvent *event);
virtual bool eventFilter(QObject *watched, QEvent *event);
bool QObject::event(QEvent *e)
{
switch (e->type()) {
case QEvent::Timer:
timerEvent((QTimerEvent*)e);
break;
case QEvent::ChildAdded:
case QEvent::ChildPolished:
case QEvent::ChildRemoved:
childEvent((QChildEvent*)e);
break;
case QEvent::DeferredDelete:
qDeleteInEventHandler(this);
break;
case QEvent::MetaCall:
{
QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(e);
QConnectionSenderSwitcher sw(this, const_cast<QObject*>(mce->sender()), mce->signalId());
mce->placeMetaCall(this);
break;
}
case QEvent::ThreadChange: {
Q_D(QObject);
QThreadData *threadData = d->threadData;
QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.load();
if (eventDispatcher) {
QList<QAbstractEventDispatcher::TimerInfo> timers = eventDispatcher->registeredTimers(this);
if (!timers.isEmpty()) {
// do not to release our timer ids back to the pool (since the timer ids are moving to a new thread).
eventDispatcher->unregisterTimers(this);
QMetaObject::invokeMethod(this, "_q_reregisterTimers", Qt::QueuedConnection,
Q_ARG(void*, (new QList<QAbstractEventDispatcher::TimerInfo>(timers))));
}
}
break;
}
default:
if (e->type() >= QEvent::User) {
customEvent(e);
break;
}
return false;
}
return true;
}
event中进行了一些事件的类别判断,然后分发到各个细分函数进行处理,像timerEvent,childEvent等,当然支持自定义事件 QEvent::User+,分发到customEvent中处理。
用户自定义数据
QObject提供了添加自定义数据的接口
static uint registerUserData();
void setUserData(uint id, QObjectUserData* data);
QObjectUserData* userData(uint id) const;
QObjectUserData是一个空类,我们继承这个类,通过registerUserData注册一个用户数据,然后setUserData设置用户数据即可,通过userData就可以提取这个用户数据了
总结
QObject提供了对象树,信号与槽机制,属性系统,事件系统,支持添加自定义数据。