Why it is not recommended to emit a large amount of data over signals in Qt?
Why then we have a option to send our own types through them?
Why it is not recommended to emit a large amount of data over signals in Qt?
Why then we have a option to send our own types through them?
Why it is not recommended to emit a large amount of data over signals in Qt?
There's no such recommendation. See this question for discussion.
First of all, when we pass data through signal arguments, we pass object instances - by value or by reference.
Yet one must discriminate between the data and the object. A QString
might contain a lot of data, but it doesn't imply that it will copy the data when you copy the string object.
// one million worth of 'a's, about 2 megabytes worth of data
const QString large1(1000*1000, QLatin1Char('a'));
large1
is an object of a QString
type. It happens that the QString
implementation uses implicit data sharing, and copying the object does not copy the data. So, copying a string is cheap, although it's still more expensive than copying a pointer value.
Now let's consider another string type:
// one million worth of 'a's
const std::string large2(1000*1000, 'a');
large2
is an object of a std::string
type. Most implementations don't use implicit data sharing, and copying the object will copy the data.
There are three cases in the signal-slot system that will force an object to be copied:
At the time of signal emission, when the signal's parameter type is a value type. E.g.:
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
At the time of invoking the slot, when the slot's parameter type is a value type. Examples are same as above.
Each time a queued connection is used to call the slot.
This happens when you select a queued connection type, or when the connection is automatic (by default), and the receiver object lives in another thread at the time the signal is emitted.
Suppose we have the following class:
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
};
We can now try multiple combinations of signals and slots. Assume that in each case the signal is emitted N
times, and it's connected to M
slots:
Number of Copies of Object, Direct Connection
signal1 signal2 signal3 signal4
slot1 0 N - -
slot2 N*M N*(M+1) - -
slot3 - - 0 N
slot4 - - N*M N*(M+1)
Number of Copies of Data, Direct Connection
signal1 signal2 signal3 signal4
slot1 0 0 - -
slot2 0 0 - -
slot3 - - 0 N
slot4 - - N*M N*(M+1)
Number of Copies of Object, Queued Connection
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)
Number of Copies of Data, Queued Connection
signal1 signal2 signal3 signal4
slot1 0 0 - -
slot2 0 0 - -
slot3 - - N*M N*(M+1)
slot4 - - 2*N*M 2*N*(M+1)
See this answer for a test case.
Why it is not recommended to emit a large amount of data over signals in Qt?
Because it usually involves copying the data, although you could pass by reference as well. And copying of data takes time. If you chose to pass by reference you must keep object lifetime in mind, otherwise you end up with a dangling reference and a crash. So passing by value is safer in this regard.
Why then we have a option to send our own types through them?
Because otherwise how will you send data? If there wasn't an option to do anything computationally expensive, you wouldn't be able to do much with a computer, would you?
Qt uses implicit sharing for most of its container classes, QImage
included. This means when you pass by value, the actual image data is not copied, only a shallow copy is made, which is much cheaper.
Qt has some examples where you can see a QImage
sent via signal/slot mechanism. Check this. There is no problem with that.
And some classes, like QCameraImageCapture
, provide signals with QImage
as parameter. I.e:
void QCameraImageCapture::imageCaptured(int id, const QImage & preview)
Reading the answer in the link you provided, they say that sending large objects via signals by value might decrease the performance of your program. And it's true. But the problem is not the signal: in C++ you should pass large objects by reference, not by value.