C++ 不建议通过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中通过信号发出大量数据


那么为什么我们可以选择通过它们发送我们自己的类型呢?

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类来说,它足够便宜。“基本上毫无用处”的答案明确警告了悬空引用的危险。你知道原子的意思是“在一个时钟周期内发生”吗?原子的意思不是“在一个时钟周期内”,远远不是。很多事情都可能发生在原子操作周围,甚至在核心之间穿梭缓存线。原子操作仅仅意味着从机器代码的角度来看,该操作具有原子语义。原子性中没有时钟周期,原子操作几乎总是比非原子操作更昂贵