QMetaObject类的实现
周末就这样过去了,都还没有一点假日的感觉,明天又该6点起床,开始我新的一周了。这个学期过的真快,转眼就是8周的时间,自己收获了很多,但是总觉得时间不够用,需要继续努力了。
今天的主要是分析QMetaObject类的实现,这样来详细的理解Qt信号与槽的机制。
首先找到4.6中QMetaObject的定义,比较吃惊的是,这个之前一直被我称为类的东西,其声明居然是一个结构体,虽然在C++中,结构体中已经可以包含函数。但是想不明白为什么这里会用结构体来声明该结构。下面是该结构体的声明:
struct Q_CORE_EXPORT QMetaObject
还是继续往下看吧,也许会有新的发现。下面是一些方面的定义,没有找到头绪,于是转向这些方法的实现文件,在这里,看到了关于该类的一些说明,这里我摘录了一些:
The QMetaObject class contains meta-information about Qt objects.
A single QMetaObject instance is created for each QObject subclass that is used in an application, and this instance stores all the meta-information for the QObject subclass. This object is available as QObject::metaObject().
This class is not normally required for application programming, but it is useful if you write meta-applications, such as scripting engines or GUI builders.
/list
/o className() returns the name of a class.
/o superClass() returns the superclass's meta-object.
/o method() and methodCount() provide information
about a class's meta-methods (signals, slots and other
/l{Q_INVOKABLE}{invokable} member functions).
/o enumerator() and enumeratorCount() and provide information about
a class's enumerators.
/o propertyCount() and property() provide information about a
class's properties.
/o constructor() and constructorCount() provide information
about a class's meta-constructors.
/endlist
这些都是对QMetaObject的一定解释,简单了解一下就好。我们回到元对象编译器为我们生成的文件中来,可以看到,我们所定义的信号都被解释成了一个函数:
void MainWindow::hello()
{
QMetaObject::activate(this, &staticMetaObject, 0, 0);
}
我们调用emit的时候,实际上就是调用了该函数。后续的槽被调用工作,就是由activate函数完成的了。
这里,我们需要特比关注一下emit这个名字,它起到了什么作用。我们转到它的定义:
#ifndef QT_NO_EMIT
# define emit
#endif
发现它竟然如此简单,这时候,不由得让我猜想,该名称只是一个标识而已,而我们如果单纯的调用hello()这个函数,也能达到相同的效果。于是,我去掉了发送先好前的emit,发现,槽也被成功的调用了。原来, emit这个名字仅仅是一个标识而已。
与此同时,我还看到了下面的定义:
// The following macros are our "extensions" to C++
// They are used, strictly speaking, only by the moc.
#ifndef Q_MOC_RUN
# if defined(QT_NO_KEYWORDS)
# define QT_NO_EMIT
# else
# define slots
# define signals protected
# endif
原来Qt的所有signals 和 slots居然什么都不是,经过宏定义替换之后,slots什么都没有了,signals变成了protected.而这两个定义,应该是为了元对象编译器做标识用的。
那么,关于信号与槽的实现,关键的入口点还有两个,一个是connect函数做了些什么,另外一个就是activate函数做了什么。
首先下下connect函数的实现:
inline bool QObject::connect(const QObject *asender, const char *asignal,
const char *amember, Qt::ConnectionType atype) const
{ return connect(asender, asignal, this, amember, atype); }
该函数将当前对象的指针传递给标准的实现函数,那么转到真正的实现看一下:
bool QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type)
在这个定义之前,我看到了一段注释,下面做简要的摘录:
/threadsafe
Creates a connection of the given /a type from the /a signal in
the /a sender object to the /a method in the /a receiver object.
Returns true if the connection succeeds; otherwise returns false.
You must use the /c SIGNAL() and /c SLOT() macros when specifying
the /a signal and the /a method, for example:
/snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 22
A signal can also be connected to another signal:
A signal can be connected to many slots and signals. Many signals
can be connected to one slot.
If a signal is connected to several slots, the slots are activated
in the same order as the order the connection was made, when the
signal is emitted.
By default, a signal is emitted for every connection you make;
two signals are emitted for duplicate connections. You can break
all of these connections with a single disconnect() call.
If you pass the Qt::UniqueConnection /a type, the connection will only
be made if it is not a duplicate. If there is already a duplicate
(exact same signal to the exact same slot on the same objects),
the connection will fail and connect will return false
The optional /a type parameter describes the type of connection
to establish. In particular, it determines whether a particular
signal is delivered to a slot immediately or queued for delivery
at a later time. If the signal is queued, the parameters must be
of types that are known to Qt's meta-object system, because Qt
needs to copy the arguments to store them in an event behind the
scenes. If you try to use a queued connection and get the error
message
call qRegisterMetaType() to register the data type before you
establish the connection.
感觉这些东西都是很有用的,可以好好看一下,里面有很多是之前都没有考虑过的细节。接下来,我们看下QObject类中connect函数是如何实现的:
{
const void *cbdata[] = { sender, signal, receiver, method, &type };
if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
return true;
}
这里做了一个对信号和槽的判断,这个QInternal之前从来没有见过,本来想看个究竟的,但是今天时间不允许了,只能到这里的。明天继续探索喽!去睡觉了,明天早上还要6点起床呢!
2009年10月18日星期日 23:54