C++ 不建议通过Qt信号发出大量数据
为什么不建议在Qt中通过信号发出大量数据C++ 不建议通过Qt信号发出大量数据,c++,qt,signals,qt5,C++,Qt,Signals,Qt5,为什么不建议在Qt中通过信号发出大量数据 那么为什么我们可以选择通过它们发送我们自己的类型呢?Qt有一些示例,您可以看到通过信号/插槽机制发送的QImage。检查。这没有问题 一些类,如QCameraImageCapture,提供了以QImage作为参数的信号。即: void QCameraImageCapture::imagecapture(int-id、const-QImage和preview) 阅读您提供的答案,他们说通过信号按值发送大型对象可能会降低程序的性能。这是真的。但是问题不是信号
那么为什么我们可以选择通过它们发送我们自己的类型呢?Qt有一些示例,您可以看到通过信号/插槽机制发送的
QImage
。检查。这没有问题
一些类,如QCameraImageCapture
,提供了以QImage
作为参数的信号。即:
void QCameraImageCapture::imagecapture(int-id、const-QImage和preview)
阅读您提供的答案,他们说通过信号按值发送大型对象可能会降低程序的性能。这是真的。但是问题不是信号:在C++中,你应该通过引用传递大对象,而不是通过值。
为什么不建议通过信号发送大量数据
在Qt
因为它通常涉及复制数据,尽管您也可以通过引用进行传递。而且复制数据需要时间。如果选择按引用传递,则必须记住对象的生存期,否则将导致引用悬空和崩溃。因此,在这方面,按值传递更安全
为什么我们可以选择通过它们发送自己的类型
否则,你将如何发送数据?如果没有一个选项来做任何计算昂贵的事情,你就不能用计算机做很多事情,是吗
Qt使用它的大多数容器类,包括QImage
。这意味着当您传递值时,实际的图像数据不会被复制,只会进行浅层复制,这要便宜得多
为什么不建议在Qt中通过信号发出大量数据
没有这样的建议。请参阅以进行讨论
数据与对象
首先,当我们通过信号参数传递数据时,我们通过值或引用传递对象实例
然而,人们必须区分数据和对象。QString
可能包含大量数据,但这并不意味着它将在复制字符串对象时复制数据
// one million worth of 'a's, about 2 megabytes worth of data
const QString large1(1000*1000, QLatin1Char('a'));
large1
是QString
类型的对象。QString
实现使用隐式数据共享,复制对象不会复制数据。因此,复制字符串是便宜的,尽管它仍然比复制指针值更昂贵
现在让我们考虑另一种字符串类型:
// one million worth of 'a's
const std::string large2(1000*1000, 'a');
large2
是std::string
类型的对象。大多数实现不使用隐式数据共享,复制对象将复制数据
复制什么时候发生?
信号插槽系统中有三种情况会强制复制对象:
Q_SIGNAL void mySignal(std::string); // copies or moves the object and data
Q_SIGNAL void mySignal(const std::string &); // no copies here
Q_SIGNAL void mySignal(QString); // copies or moves the object, but not the data
Q_SIGNAL void mySignal(const QString &); // no copies here
class C : public QObject {
public:
Q_SIGNAL void signal1(const QString &); // correct
Q_SIGNAL void signal2(QString); // don't do that
Q_SIGNAL void signal3(const std::string &); // correct
Q_SIGNAL void signal4(std::string); // really don't do that
Q_SLOT void slot1(const QString &); // correct
Q_SLOT void slot2(QString); // only do that if you need a value to modify
Q_SLOT void slot3(const std::string &); // correct
Q_SLOT void slot4(std::string); // only do that if you need a value to modify
};
我们现在可以尝试信号和插槽的多种组合。假设在每种情况下,信号发射N次
,并连接到M
插槽:
对象的拷贝数,直接连接
signal1 signal2 signal3 signal4
slot1 0 N - -
slot2 N*M N*(M+1) - -
slot3 - - 0 N
slot4 - - N*M N*(M+1)
signal1 signal2 signal3 signal4
slot1 0 0 - -
slot2 0 0 - -
slot3 - - 0 N
slot4 - - N*M N*(M+1)
signal1 signal2 signal3 signal4
slot1 N*M N*(M+1) - -
slot2 2*N*M 2*N*(M+1) - -
slot3 - - N*M N*(M+1)
slot4 - - 2*N*M 2*N*(M+1)
signal1 signal2 signal3 signal4
slot1 0 0 - -
slot2 0 0 - -
slot3 - - N*M N*(M+1)
slot4 - - 2*N*M 2*N*(M+1)
直接连接的数据拷贝数
signal1 signal2 signal3 signal4
slot1 0 N - -
slot2 N*M N*(M+1) - -
slot3 - - 0 N
slot4 - - N*M N*(M+1)
signal1 signal2 signal3 signal4
slot1 0 0 - -
slot2 0 0 - -
slot3 - - 0 N
slot4 - - N*M N*(M+1)
signal1 signal2 signal3 signal4
slot1 N*M N*(M+1) - -
slot2 2*N*M 2*N*(M+1) - -
slot3 - - N*M N*(M+1)
slot4 - - 2*N*M 2*N*(M+1)
signal1 signal2 signal3 signal4
slot1 0 0 - -
slot2 0 0 - -
slot3 - - N*M N*(M+1)
slot4 - - 2*N*M 2*N*(M+1)
对象、排队连接的副本数
signal1 signal2 signal3 signal4
slot1 0 N - -
slot2 N*M N*(M+1) - -
slot3 - - 0 N
slot4 - - N*M N*(M+1)
signal1 signal2 signal3 signal4
slot1 0 0 - -
slot2 0 0 - -
slot3 - - 0 N
slot4 - - N*M N*(M+1)
signal1 signal2 signal3 signal4
slot1 N*M N*(M+1) - -
slot2 2*N*M 2*N*(M+1) - -
slot3 - - N*M N*(M+1)
slot4 - - 2*N*M 2*N*(M+1)
signal1 signal2 signal3 signal4
slot1 0 0 - -
slot2 0 0 - -
slot3 - - N*M N*(M+1)
slot4 - - 2*N*M 2*N*(M+1)
排队连接的数据拷贝数
signal1 signal2 signal3 signal4
slot1 0 N - -
slot2 N*M N*(M+1) - -
slot3 - - 0 N
slot4 - - N*M N*(M+1)
signal1 signal2 signal3 signal4
slot1 0 0 - -
slot2 0 0 - -
slot3 - - 0 N
slot4 - - N*M N*(M+1)
signal1 signal2 signal3 signal4
slot1 N*M N*(M+1) - -
slot2 2*N*M 2*N*(M+1) - -
slot3 - - N*M N*(M+1)
slot4 - - 2*N*M 2*N*(M+1)
signal1 signal2 signal3 signal4
slot1 0 0 - -
slot2 0 0 - -
slot3 - - N*M N*(M+1)
slot4 - - 2*N*M 2*N*(M+1)
请参阅以获取测试用例。您在哪里读到的?您所说的“大量”是什么意思?@Lazar-当您通过值传递QImage时,只会出现浅拷贝。所以它并没有那么重。不会复制实际图像数据。这听起来不像是特定于信号/插槽,甚至不是Qt5。它归结为一个简单的事实:复制数据不是免费的。好吧,d'uh,这真是一个惊喜。@Lazar-read关于Qt中的隐式共享当您按值传递图像时,图像数据不会被复制。“通常涉及复制数据”是错误的。它不是“通常”,而是“总是”或“从不”,我们不是在谈论复制“数据”,而是复制对象实例:这样的副本是否会复制底层“数据”取决于实现!鉴于这些缺点,这个答案基本上是无用的。@KubaOber-你得到的答案是错误的。“通常涉及复制数据”意味着在发出数据时“通常通过值传递”,但也可以通过引用发出。现在明白了吗?为什么在发射时“通常按值传递”?这是对任何类型(数值类型和指针除外)的毫无意义的过早悲观。即使按值传递
QString
也不如按引用传递便宜,因为副本将强制原子引用计数递增。如果检查Qt本身,则有零(0)个信号签名通过值传递大型对象。用这种方式编写代码是完全没有意义的,因为您使用的是Qt,它更安全,因为Qt使用CoW,对于CoW类来说,它足够便宜。“基本上毫无用处”的答案明确警告了悬空引用的危险。你知道原子的意思是“在一个时钟周期内发生”吗?原子的意思不是“在一个时钟周期内”,远远不是。很多事情都可能发生在原子操作周围,甚至在核心之间穿梭缓存线。原子操作仅仅意味着从机器代码的角度来看,该操作具有原子语义。原子性中没有时钟周期,原子操作几乎总是比非原子操作更昂贵