之前一直在纠结Qt的信号槽是否要传引用,如果传引用会不会有悬挂引用的问题。
故参考Copied or Not Copied: Arguments in Signal-Slot Connections?这篇博客测试一下
CopyTest.h
#include <QObject>
class CCopyable
{
public:
// qRegisterMetaType的类需要默认构造函数
CCopyable();
CCopyable(int id, const QString &name);
CCopyable(const CCopyable &rhs);
~CCopyable();
CCopyable &operator=(const CCopyable &rhs);
private:
int m_id;
QString m_name;
};
class CSender : public QObject
{
Q_OBJECT
public:
CSender(QObject *parent = nullptr);
~CSender() {}
void sendSignal();
signals:
void sendConstRef(const CCopyable &c);
void sendValue(CCopyable c);
void testSig(QString);
void testSig(QString, int);
//void testSig(const QString&);
};
class CReceiver : public QObject
{
Q_OBJECT
public:
CReceiver(QObject *parent = nullptr);
~CReceiver() {}
public slots:
void receiveConstRef(const CCopyable &c);
void receiveValue(CCopyable c);
};
void signalSlotCopyTest();
CopyTest.cpp
#include "CopyTest.h"
#include <QDebug>
#include <QThread>
#include <QTimer>
#define CaseNum 4
#define EnableQueuedConnection
CCopyable::CCopyable()
{
}
CCopyable::CCopyable(int id, const QString &name)
: m_id(id)
, m_name(name)
{
}
CCopyable::~CCopyable()
{
}
CCopyable::CCopyable(const CCopyable &rhs)
{
qDebug() << "copy construct";
m_id = rhs.m_id;
m_name = rhs.m_name;
}
CCopyable& CCopyable::operator=(const CCopyable &rhs)
{
qDebug() << "assign";
if (this == &rhs)
{
return *this;
}
m_id = rhs.m_id;
m_name = rhs.m_name;
return *this;
}
CSender::CSender(QObject *parent)
: QObject(parent)
{
}
CReceiver::CReceiver(QObject * parent)
: QObject(parent)
{
QThread *t = new QThread;
moveToThread(t);
t->start();
}
void CReceiver::receiveConstRef(const CCopyable &c)
{
qDebug() << "receiveConstRef";
}
void CReceiver::receiveValue(CCopyable c)
{
qDebug() << "receiveValue";
}
void signalSlotCopyTest()
{
CSender *sender = new CSender;
CReceiver* receiver = new CReceiver;
#ifdef EnableQueuedConnection
qRegisterMetaType<CCopyable>("CCopyable");
Qt::ConnectionType ctype = Qt::QueuedConnection;
#else
Qt::ConnectionType ctype = Qt::DirectConnection;
#endif // EnableQueuedConnection
switch (CaseNum)
{
case 1:
QObject::connect(sender, &CSender::sendConstRef, receiver, &CReceiver::receiveConstRef, ctype);
break;
case 2:
QObject::connect(sender, &CSender::sendValue, receiver, &CReceiver::receiveConstRef, ctype);
break;
case 3:
QObject::connect(sender, &CSender::sendConstRef, receiver, &CReceiver::receiveValue, ctype);
break;
case 4:
QObject::connect(sender, &CSender::sendValue, receiver, &CReceiver::receiveValue, ctype);
break;
default:
break;
}
QTimer::singleShot(1000, [sender]() {
sender->sendSignal();
});
}
void CSender::sendSignal()
{
CCopyable copyable(1,"momo");
switch (CaseNum)
{
case 1:
emit sendConstRef(copyable);
break;
case 2:
emit sendValue(copyable);
break;
case 3:
emit sendConstRef(copyable);
break;
case 4:
emit sendValue(copyable);
break;
default:
break;
}
}
测试结果跟参考博客一样:
如果信号和槽参数都传值的话,会多调用两次拷贝构造函数。而如果信号槽连接方式是Qt::QueuedConnection的话,又会多调用一次拷贝构造函数。
所以结论就是:
- Qt为了避免队列连接造成的悬挂引用问题,会拷贝一次参数。跨线程调用槽(且是Qt::QueuedConnection)的话这一次拷贝无法避免
- 能传引用就传引用,避免参数多两次拷贝