1. 隐式共享
之前一直担心Qt中的信号槽在传递大量数据下的情况下复制拷贝产生的效率问题,还转而使用指针去传递。实际上因为Qt中的隐式共享的技术完全不用担心此问题。不过值得注意的是隐式共享的条件和使用规则,而且也并非所有的结构都支持隐式共享。
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QByteArray b1 = "hello world";
QByteArray b2 = b1;
qDebug() << "0x" + QString::number(reinterpret_cast<qintptr>(b1.constData()),16);
qDebug() << "0x" + QString::number(reinterpret_cast<qintptr>(b2.constData()),16);
b2 = "你好,世界";
qDebug() << Qt::endl;
qDebug() << "0x" + QString::number(reinterpret_cast<qintptr>(b1.constData()),16);
qDebug() << "0x" + QString::number(reinterpret_cast<qintptr>(b2.constData()),16);
return a.exec();
}
输出:
"0x1b6558"
"0x1b6558"
"0x1b6558"
"0x1b7d68"
这里定义了两个变量,当把b1直接复制给b2时采用浅拷贝,两个变量的数据地址相同。修改b2的内容时,采用深拷贝。
2.信号槽中的隐式共享
#include <QCoreApplication>
#include <QDebug>
class Test : public QObject {
Q_OBJECT
public:
void received(QByteArray byte) {
qDebug() << Q_FUNC_INFO << "0x" + QString::number(reinterpret_cast<qintptr>(byte.constData()),16);
byte = "x";
qDebug() << Q_FUNC_INFO << "0x" + QString::number(reinterpret_cast<qintptr>(byte.constData()),16);
}
signals:
void sigByte(QByteArray byte);
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QByteArray b1 = "hello world";
qDebug() << Q_FUNC_INFO << "0x" + QString::number(reinterpret_cast<qintptr>(b1.constData()),16);
Test t;
QObject::connect(&t,&Test::sigByte,&t,&Test::received);
emit t.sigByte(b1);
return a.exec();
}
#include "main.moc"
输出:
int main(int, char**) "0xf66558"
void Test::received(QByteArray) "0xf66558"
void Test::received(QByteArray) "0xf67df8"
同理信号槽中也是可以使用隐式共享的,其效果也是一样的。所以根本不用担心信号槽中传输大量数据下的效率问题。
3.多线程中信号槽的隐式共享
#include <QCoreApplication>
#include <QDebug>
#include <QThread>
class Test : public QObject {
Q_OBJECT
public:
void received(QByteArray byte) {
qDebug() << QThread::currentThread() << Q_FUNC_INFO << "0x" + QString::number(reinterpret_cast<qintptr>(byte.constData()),16);
byte = "x";
qDebug() << QThread::currentThread() << Q_FUNC_INFO << "0x" + QString::number(reinterpret_cast<qintptr>(byte.constData()),16);
}
signals:
void sigByte(QByteArray byte);
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QByteArray b1 = "hello world";
qDebug() << QThread::currentThread() << Q_FUNC_INFO << "0x" + QString::number(reinterpret_cast<qintptr>(b1.constData()),16);
Test t;
QObject::connect(&t,&Test::sigByte,&t,&Test::received);
QThread th;
t.moveToThread(&th);
th.start();
emit t.sigByte(b1);
return a.exec();
}
#include "main.moc"
输出:
QThread(0xdd5ff0) int main(int, char**) "0xdd6558"
QThread(0x69fd60) void Test::received(QByteArray) "0xdd6558"
QThread(0x69fd60) void Test::received(QByteArray) "0x2593a48"
可以看到多线程中信号槽的数据传递也是支持隐式共享的。
4.结论
无论是多线程还是单线程中信号槽的数据传递都是支持隐式共享的,所以也不用担心大数据下的效率问题。
不过笔者这里只试验了QByteArray,未对其它数据结构验证,使用时请先查看Qt官方手册或自行测试。
建议在不需要进行数据更改的地方使用常引用const QByteArray&
的方式传递,这样在信号槽的直接连接时构造临时变量的开销也可以省掉。
多线程中也可以使用常引用的方式传递,因为是异步通信,Qt会在信号传递时生成临时变量,不过内部数据依然是浅拷贝不需要担心。在数据没有被任何变量引用时进行析构。