При компиляции проекта в какой-то момент появляется утилита moc.exe - это так называемый препроцессор , который обрабатывает файлы h в которых прописаны СПЕЦИАЛЬНЫЕ макросы. Это основная парадигма Qt - создание механизма сигнал /слот. (Class QObject)
Вот первое ее появление при сборки QT из исходников:
mingw32-make.exe[2]: Entering directory `C:/QtSDK1.2.1/QtSources/4.8.1/src/corelib'
C:\QtSDK1.2.1\QtSources\4.8.1\bin\moc.exe -DQT_THREAD_SUPPORT -DUNICODE -DQT_LARGEFILE_SUPPORT -DQT_BUILD_CORE_LIB -DQT_NO_USING_NAMESPACE -DQT_ASCII_CAST_WARNINGS -DQT_MOC_COMPAT -DQT_USE_QSTRINGBUILDER -D_USE_MATH_DEFINES -DQLIBRARYINFO_EPOCROOT -DHB_EXPORT=Q_CORE_EXPORT -DQT_NO_DEBUG -DQT_NO_DYNAMIC_CAST -I"..\..\include" -I"..\..\include\QtCore" -I"tmp\rcc\release_static" -I"tmp" -I"global" -I"..\..\tools\shared" -I"..\3rdparty\zlib" -I"..\3rdparty\harfbuzz\src" -I"..\3rdparty\md5" -I"..\3rdparty\md4" -I"..\..\include\ActiveQt" -I"tmp\moc\release_static" -I"c:\QtSDK1.2.1\Desktop\Qt\4.8.1\mingw\mkspecs\win32-
g++" -D__GNUC__ -DWIN32 animation\qabstractanimation.h -o tmp\moc\release_static\moc_qabstractanimation.cpp
Что делает moc.exe (при первом рассмотрении) . Он видим что у qabstractanimation.h класс наследуется от QObject и создает на основании этого в специальном каталоге файл cpp файл tmp\moc\release_static\moc_qabstractanimation.cpp, который становится частью вашего проекта.
На самом деле , если посмотреть файл moc_qabstractanimation.cpp , то практически сразу все становится понятно :
Вот мы имеем в файле qabstractanimation.h определение класса QAbstractAnimation , унаследованного от QObject. MOC реагирует на (QObject) и создает файл moc_qabstractanimation.cpp.
class Q_CORE_EXPORT QAbstractAnimation : public QObject
{
Q_OBJECT /// - это обязательный МАКРОС
Q_ENUMS(State)
Q_ENUMS(Direction)
Q_PROPERTY(State state READ state NOTIFY stateChanged)
Q_PROPERTY(int loopCount READ loopCount WRITE setLoopCount)
Q_PROPERTY(int currentTime READ currentTime WRITE setCurrentTime)
Q_PROPERTY(int currentLoop READ currentLoop NOTIFY currentLoopChanged)
Q_PROPERTY(Direction direction READ direction WRITE setDirection NOTIFY directionChanged)
Q_PROPERTY(int duration READ duration)
public:
enum Direction {
Forward,
Backward
};
enum State {
Stopped,
Paused,
Running
};
enum DeletionPolicy {
KeepWhenStopped = 0,
DeleteWhenStopped
};
QAbstractAnimation(QObject *parent = 0);
virtual ~QAbstractAnimation();
State state() const;
QAnimationGroup *group() const;
Direction direction() const;
void setDirection(Direction direction);
int currentTime() const;
int currentLoopTime() const;
int loopCount() const;
void setLoopCount(int loopCount);
int currentLoop() const;
virtual int duration() const = 0;
int totalDuration() const;
Q_SIGNALS:
void finished();
void stateChanged(QAbstractAnimation::State newState, QAbstractAnimation::State oldState);
void currentLoopChanged(int currentLoop);
void directionChanged(QAbstractAnimation::Direction);
public Q_SLOTS:
void start(QAbstractAnimation::DeletionPolicy policy = KeepWhenStopped);
void pause();
void resume();
void setPaused(bool);
void stop();
void setCurrentTime(int msecs);
protected:
QAbstractAnimation(QAbstractAnimationPrivate &dd, QObject *parent = 0);
bool event(QEvent *event);
virtual void updateCurrentTime(int currentTime) = 0;
virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState);
virtual void updateDirection(QAbstractAnimation::Direction direction);
private:
Q_DISABLE_COPY(QAbstractAnimation)
Q_DECLARE_PRIVATE(QAbstractAnimation)
};
Cам moc_qabstractanimation.cpp при этом добавляется в конце к qabstractanimation.cpp (смотрите #include "moc_qabstractanimation.cpp").
В файл moc_qabstractanimation.cpp попросту добавляется дополнительный функционал к классу например реализацию функции qt_static_metacall :
Само определение функции qt_static_metacall определяется макросом Q_OBJECT (см .qobjectsdef.h) . Q_OBJECT всегда вначале класса, наследуемого от QObject;
Класс QObject определен в qobject.h :
class Q_CORE_EXPORT QObject
{
Q_OBJECT /// вот он !!!!
Q_PROPERTY(QString objectName READ objectName WRITE setObjectName)
Q_DECLARE_PRIVATE(QObject)
public:
Q_INVOKABLE explicit QObject(QObject *parent=0);
virtual ~QObject();
...............................
Q_SIGNALS:
void destroyed(QObject * = 0);
public:
inline QObject *parent() const { return d_ptr->parent; }
inline bool inherits(const char *classname) const
{ return const_cast<QObject *>(this)->qt_metacast(classname) != 0; }
public Q_SLOTS:
void deleteLater();
protected:
.......................................
private:
Q_DISABLE_COPY(QObject)
Q_PRIVATE_SLOT(d_func(), void _q_reregisterTimers(void *))
};
У каждого класса наследника QObject первая строка в классе это макрос Q_OBJECT
#define Q_OBJECT \
public: \
Q_OBJECT_CHECK \
static const QMetaObject staticMetaObject; \
Q_OBJECT_GETSTATICMETAOBJECT \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
QT_TR_FUNCTIONS \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
private: \
Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; \
Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
...................
Очень похоже , что вся идея в том , чтобы для объекта (наследника QObject) пронумеровать его свойства , составить ему таблицу соответствия номер свойства = наименование. Далее в любом месте программы можно вызвать прочитать свойтсво объекта по его наименованию. Ничего не напоминает? Это реализация собственного скриптового языка.
static const char qt_meta_stringdata_QAbstractAnimation[] = {
"QAbstractAnimation\0\0finished()\0"
"newState,oldState\0"
"stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)\0"
"currentLoop\0currentLoopChanged(int)\0"
"directionChanged(QAbstractAnimation::Direction)\0"
"policy\0start(QAbstractAnimation::DeletionPolicy)\0"
"start()\0pause()\0resume()\0setPaused(bool)\0"
"stop()\0msecs\0setCurrentTime(int)\0State\0"
"state\0int\0loopCount\0currentTime\0"
"Direction\0direction\0duration\0Forward\0"
"Backward\0Stopped\0Paused\0Running\0"
};
void QAbstractAnimation::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
Q_ASSERT(staticMetaObject.cast(_o));
QAbstractAnimation *_t = static_cast<QAbstractAnimation *>(_o);
switch (_id) {
case 0: _t->finished(); break;
case 1: _t->stateChanged((*reinterpret_cast< QAbstractAnimation::State(*)>(_a[1])),(*reinterpret_cast< QAbstractAnimation::State(*)>(_a[2]))); break;
case 2: _t->currentLoopChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
case 3: _t->directionChanged((*reinterpret_cast< QAbstractAnimation::Direction(*)>(_a[1]))); break;
case 4: _t->start((*reinterpret_cast< QAbstractAnimation::DeletionPolicy(*)>(_a[1]))); break;
case 5: _t->start(); break;
case 6: _t->pause(); break;
case 7: _t->resume(); break;
case 8: _t->setPaused((*reinterpret_cast< bool(*)>(_a[1]))); break;
case 9: _t->stop(); break;
case 10: _t->setCurrentTime((*reinterpret_cast< int(*)>(_a[1]))); break;
default: ;
}
}
}
У каждого класса наследника QObject есть (должен быть) в начале макрос Q_OBJECT . А значит у кажого такого класса есть :
static const QMetaObject staticMetaObject; // это структура
Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); // это функция qt_static_metacall реализацией которой занимается MOC . qt_static_metacall вызывает функцию вашего объекта по номеру _id.
Структура QMetaObject на самом деле имеет много переменных и функций.
struct Q_CORE_EXPORT QMetaObject
{
const char *className() const;
const QMetaObject *superClass() const;
QObject *cast(QObject *obj) const;
const QObject *cast(const QObject *obj) const;
#ifndef QT_NO_TRANSLATION
// ### Qt 4: Merge overloads
QString tr(const char *s, const char *c) const;
QString trUtf8(const char *s, const char *c) const;
QString tr(const char *s, const char *c, int n) const;
QString trUtf8(const char *s, const char *c, int n) const;
#endif // QT_NO_TRANSLATION
int methodOffset() const;
int enumeratorOffset() const;
int propertyOffset() const;
int classInfoOffset() const;
int constructorCount() const;
int methodCount() const;
int enumeratorCount() const;
int propertyCount() const;
int classInfoCount() const;
int indexOfConstructor(const char *constructor) const;
int indexOfMethod(const char *method) const;
int indexOfSignal(const char *signal) const;
int indexOfSlot(const char *slot) const;
int indexOfEnumerator(const char *name) const;
int indexOfProperty(const char *name) const;
int indexOfClassInfo(const char *name) const;
QMetaMethod constructor(int index) const;
QMetaMethod method(int index) const;
QMetaEnum enumerator(int index) const;
QMetaProperty property(int index) const;
QMetaClassInfo classInfo(int index) const;
QMetaProperty userProperty() const;
static bool checkConnectArgs(const char *signal, const char *method);
static QByteArray normalizedSignature(const char *method);
static QByteArray normalizedType(const char *type);
// internal index-based connect
static bool connect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index,
int type = 0, int *types = 0);
// internal index-based disconnect
static bool disconnect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index);
static bool disconnectOne(const QObject *sender, int signal_index,
const QObject *receiver, int method_index);
// internal slot-name based connect
static void connectSlotsByName(QObject *o);
// internal index-based signal activation
static void activate(QObject *sender, int signal_index, void **argv); //obsolete
static void activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv); //obsolete
static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);
static void activate(QObject *sender, const QMetaObject *, int from_local_signal_index, int to_local_signal_index, void **argv); //obsolete
// internal guarded pointers
static void addGuard(QObject **ptr);
static void removeGuard(QObject **ptr);
static void changeGuard(QObject **ptr, QObject *o);
static bool invokeMethod(QObject *obj, const char *member,
Qt::ConnectionType,
QGenericReturnArgument ret,
QGenericArgument val0 = QGenericArgument(0),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument());
static inline bool invokeMethod(QObject *obj, const char *member,
QGenericReturnArgument ret,
QGenericArgument val0 = QGenericArgument(0),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument())
{
return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3,
val4, val5, val6, val7, val8, val9);
}
static inline bool invokeMethod(QObject *obj, const char *member,
Qt::ConnectionType type,
QGenericArgument val0 = QGenericArgument(0),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument())
{
return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2,
val3, val4, val5, val6, val7, val8, val9);
}
static inline bool invokeMethod(QObject *obj, const char *member,
QGenericArgument val0 = QGenericArgument(0),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument())
{
return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0,
val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
QObject *newInstance(QGenericArgument val0 = QGenericArgument(0),
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
QGenericArgument val4 = QGenericArgument(),
QGenericArgument val5 = QGenericArgument(),
QGenericArgument val6 = QGenericArgument(),
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument()) const;
enum Call {
InvokeMetaMethod,
ReadProperty,
WriteProperty,
ResetProperty,
QueryPropertyDesignable,
QueryPropertyScriptable,
QueryPropertyStored,
QueryPropertyEditable,
QueryPropertyUser,
CreateInstance
};
int static_metacall(Call, int, void **) const;
static int metacall(QObject *, Call, int, void **);
#ifdef QT3_SUPPORT
QT3_SUPPORT const char *superClassName() const;
#endif
struct { // private data
const QMetaObject *superdata;
const char *stringdata;
const uint *data;
const void *extradata;
} d;
};
Теперь как связать два независимых объекта ?... Один объект создан в коде ранее (и размещен уже где-то в памяти RAM ) , второй объект создается и тоже размещается в памяти RAM. Например в конструкторе можно вызать функцию connect (QMetaObject структуры ).
Мы знаем уже адреса в памяти sender и receiver объекта (мы на стадии сборки) . Все наследники класса QObject. Остается только указать индексы (номера) функций - какие мы свяжем. Понятно , что состав переменных (параметров) у обоих функций имеет значение и должен быть идентичен.
bool QMetaObject::connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, int type, int *types)
{
signal_index = methodIndexToSignalIndex(sender->metaObject(), signal_index);
return QMetaObjectPrivate::connect(sender, signal_index,
receiver, method_index,
0, //FIXME, we could speed this connection up by computing the relative index
type, types);
}
По-видимому и все..