Думаю, мне следует добавить следующее.
Есть еще один связанный вопрос - и очень хорошая статья , которую можно рассматривать как довольно подробное расширение к ответу ; вот снова эта статья с улучшенной (хотя и не идеальной) подсветкой синтаксиса кода.
Вот мой короткий пересказ, который может быть склонен к ошибкам)
Обычно, когда мы вставляем макрос Q_OBJECT
в определение нашего класса, препроцессор расширяет его до статического объявления экземпляра QMetaObject
, которое будет общим для всех экземпляров одного и того же класса:
class ClassName : public QObject // our class definition
{
static const QMetaObject staticMetaObject; // <--= Q_OBJECT results to this
// ... signal and slots definitions, other stuff ...
}
Этот экземпляр, в свою очередь, при инициализации будет хранить сигнатур ("methodname(argtype1,argtype2)"
) сигналов и слотов, что позволит реализовать вызов indexOfMethod()
, который возвращает, ну, в общем, метод индекс по строке подписи:
struct Q_CORE_EXPORT QMetaObject
{
// ... skip ...
int indexOfMethod(const char *method) const;
// ... skip ...
static void activate(QObject *sender, int signal_index, void **argv);
// ... skip ...
struct { // private data
const QMetaObject *superdata; // links to the parent class, I guess
const char *stringdata; // basically, "string1\0string2\0..." that contains signatures and other names
const uint *data; // the indices for the strings in stringdata and other stuff (e.g. flags)
// skip
} d;
};
Теперь, когда moc
создает файл moc_headername.cpp
для заголовка класса Qt headername.h
, он помещает туда строки подписи и другие данные, необходимые для правильной инициализации структуры d
, а затем записывает код инициализации синглтона staticMetaObject
, использующий эти данные.
Еще одна важная вещь, которую он делает, - это генерация кода для метода qt_metacall()
объекта, который принимает идентификатор метода объекта и массив указателей на аргументы и вызывает метод через длинный switch
, например:
int ClassName::qt_metacall(..., int _id, void **_args)
{
// ... skip ...
switch (_id) {
case 0: signalOrSlotMethod1(_args[1], _args[2]); break; // for a method with two args
case 1: signalOrSlotMethod2(_args[1]); break; // for a method with a single argument
// ... etc ...
}
// ... skip ...
}
Последнее, для каждого сигнала moc
генерирует реализацию, которая содержит QMetaObject::activate()
вызов:
void ClassName::signalName(argtype1 arg1, argtype2 arg2, /* ... */)
{
void *_args[] = { 0, // this entry stands for the return value
&arg1, // actually, there's a (void*) type conversion
&arg2, // in the C++ style
// ...
};
QMetaObject::activate( this,
&staticMetaObject,
0, /* this is the signal index in the qt_metacall() map, I suppose */
_args
);
}
Наконец, вызов connect()
преобразует сигнатуры строковых методов в их целочисленные идентификаторы (используемые qt_metacall()
) и поддерживает список соединений между сигналами и слотами; когда сигнал испускается, код activate()
проходит через этот список и вызывает соответствующий слот объекта через их метод qt_metacall()
.
Подводя итог, статический экземпляр QMetaObject
хранит «метаинформацию» (строки сигнатур метода и т. Д.), Сгенерированный метод qt_metacall()
предоставляет «таблицу методов», которая позволяет любому сигналу / слоту вызываться index, реализации сигналов, генерируемые moc
, используют эти индексы через activate()
, и, наконец, connect()
выполняет работу по поддержанию списка отображений индекса сигнал-слот.
* Примечание: есть сложность этой схемы, используемая для случая, когда мы хотим доставлять сигналы между различными потоками (я подозреваю, что нужно смотреть на код blocking_activate()
), но я надеюсь, что общая идея остается той же )
Это мое очень грубое понимание связанной статьи, которая может легко ошибаться, поэтому я рекомендую пойти и прочитать ее напрямую)
PS. Поскольку я хотел бы улучшить мое понимание реализации Qt, пожалуйста, сообщите мне о любых несоответствиях в моем пересказе!
Поскольку мой другой (более ранний) ответ был удален каким-то ревностным редактором, я добавлю сюда текст (мне не хватает нескольких деталей, которые были не включены в пост Павла Шведа, и я сомневаюсь в человеке, который удалил ответ позаботился.)
@ Павел Швед:
Я почти уверен, что где-то в заголовках Qt есть строка:
#define emit
Просто для подтверждения: нашел его в старом коде Qt с помощью Google Code Search. Вполне вероятно, что он все еще там); найденный путь был:
FTP: //ftp.slackware-brasil.com.br>
Slackware-7.1>
вно>
КДЭ-1,90>
кварты-2.1.1.tgz>
USR>
Lib>
кварты-2.1.1>
ЦСИ>
ядро>
qobjectdefs.h
Еще одна дополнительная ссылка: http://lists.trolltech.com/qt-interest/2007-05/thread00691-0.html - см. Ответ Андреаса Пакулата
А вот еще один ответ: Вопрос Qt: Как работают сигналы и слоты?