cpp
class Sender : public QObject
{
Q_OBJECT
signals:
// void testSignal(QString msg); // #1
// void testSignal(const QString& msg); // #2
};
如上所示,当testSignal连接的槽函数在另一个线程时,有必要将参数类型写为#1这样吗?
我们可以在moc文件中看到信号的实现:
cpp
void Sender::testSignal(const QString & _t1)
{
QMetaObject::activate<void>(this, &staticMetaObject, 1, nullptr, _t1);
}
而QMetaObject::activate的实现参考Qt6.11源代码可见:
cpp
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
void **argv)
{
int signal_index = local_signal_index + QMetaObjectPrivate::signalOffset(m);
if (Q_UNLIKELY(qt_signal_spy_callback_set.loadRelaxed()))
doActivate<true>(sender, signal_index, argv);
else
doActivate<false>(sender, signal_index, argv);
}
template <bool callbacks_enabled>
void doActivate(QObject *sender, int signal_index, void **argv)
{
...
// determine if this connection should be sent immediately or
// put into the event queue
if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
|| (c->connectionType == Qt::QueuedConnection)) {
queued_activate(sender, signal_index, c, argv);
continue;
...
}
static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)
{
...
auto ev = c->isSlotObject ?
std::make_unique<QQueuedMetaCallEvent>(c->slotObj,
sender, signal, nargs, argTypes.data(), argv) :
std::make_unique<QQueuedMetaCallEvent>(c->method_offset, c->method_relative, c->callFunction,
sender, signal, nargs, argTypes.data(), argv);
...
}
可见异步信号槽将信号包装成了QQueuedMetaCallEvent,而QQueuedMetaCallEvent的构造函数如下:
cpp
QQueuedMetaCallEvent::QQueuedMetaCallEvent(ushort method_offset, ushort method_relative,
QObjectPrivate::StaticMetaCallFunction callFunction,
const QObject *sender, int signalId, int argCount,
const QtPrivate::QMetaTypeInterface * const *argTypes,
const void * const *argValues)
: QMetaCallEvent(sender, signalId, {nullptr, nullptr, callFunction, argCount,
method_offset, method_relative}),
prealloc_()
{
copyArgValues(argCount, argTypes, argValues);
}
inline void QQueuedMetaCallEvent::copyArgValues(int argCount, const QtPrivate::QMetaTypeInterface * const *argTypes,
const void * const *argValues)
{
allocArgs();
void **args = d.args_;
QMetaType *types = reinterpret_cast<QMetaType *>(d.args_ + d.nargs_);
int inplaceIndex = 0;
if (argCount) {
types[0] = QMetaType(); // return type
args[0] = nullptr; // return value pointer
}
// no return value
for (int n = 1; n < argCount; ++n) {
types[n] = QMetaType(argTypes[n]);
if (typeFitsInPlace(types[n]) && inplaceIndex < InplaceValuesCapacity) {
// Copy-construct in place
void *where = &valuesPrealloc_[inplaceIndex++].storage;
types[n].construct(where, argValues[n]);
args[n] = where;
} else {
// Allocate and copy-construct
args[n] = types[n].create(argValues[n]);
}
}
}
可见QQueuedMetaCallEvent将参数复制了一份,所以信号参数使用QString而不使用const QString&是没有必要的,反而增加了开销,而QQueuedMetaCallEvent已经将参数复制了一份,由事件循环系统将QQueuedMetaCallEvent派发到槽函数所在的线程后,也就相当于槽函数所在线程的本线程调用,槽函数参数也应当使用const QString& 而不是使用QString复制。