QThread
の内部動作は関係ありません。イベント ループの動作には関係ありません。スロットのオブジェクトとは異なるスレッドに存在する にシグナルを送信すると、シグナルは受信スレッドのイベント キューに としてポストされemit
ます。受信スレッドで実行されているイベント ループは、このイベントに基づいて動作し、送信されたシグナルに接続されたスロットへの呼び出しを実行します。QObject
QMetaCallEvent
したがって、何が起こっても、シグナルを介して送信するデータは、最終的には QEvent 派生クラスのインスタンスのペイロードになります。
問題の核心は、QMetaCallEvent
がイベント ループに到達し、コンテナーが引数としてスロットに渡されるときです。もちろん、途中で何度もコピー コンストラクタを呼び出すことができます。以下は、コピー コンストラクターとデフォルト コンストラクターが実際に呼び出される回数を示す簡単なコードです。
あなたはうれしい驚きになるでしょう:)
Qt コンテナーはコピー オン ライトで暗黙的に共有されるため、コピーの作成にかかるコストはごくわずかです。作成時に参照カウンターがアトミックにインクリメントされるだけです。たとえば、データ メンバはコピーされません。
悲しいかな、11 より前の C++ には醜い側面があります。スロット コードが何らかの方法でコンテナーを変更する場合、元のコンテナーがもう必要ないことをコンパイラーに知らせるような方法でスロットへの参照を渡す方法はありません。したがって、スロットがコンテナーへの const 参照を受け取った場合、コピーが作成されないことが保証されます。スロットがコンテナーの書き込み可能なコピーを受け取り、それを変更すると、呼び出しサイトで稼働中のインスタンスが不要になるため、完全に不要なコピーが作成されます。C++-11 では、右辺値参照をパラメーターとして渡します。関数呼び出しで右辺値参照を渡すと、呼び出し元で渡されたオブジェクトの有効期間が終了します。
サンプルコード出力:
"Started" copies: 0 assignments: 0 default instances: 0
"Created Foo" copies: 0 assignments: 0 default instances: 100
"Created Bar" copies: 0 assignments: 0 default instances: 100
"Received signal w/const container" copies: 0 assignments: 0 default instances: 100
"Received signal w/copy of the container" copies: 0 assignments: 0 default instances: 100
"Made a copy" copies: 100 assignments: 1 default instances: 101
"Reset" copies: 0 assignments: 0 default instances: 0
"Received signal w/const class" copies: 2 assignments: 0 default instances: 1
"Received signal w/copy of the class" copies: 3 assignments: 0 default instances: 1
//main.cpp
#include <QtCore>
class Class {
static QAtomicInt m_copies;
static QAtomicInt m_assignments;
static QAtomicInt m_instances;
public:
Class() { m_instances.fetchAndAddOrdered(1); }
Class(const Class &) { m_copies.fetchAndAddOrdered(1); }
Class & operator=(const Class &) { m_assignments.fetchAndAddOrdered(1); return *this; }
static void dump(const QString & s = QString()) {
qDebug() << s << "copies:" << m_copies << "assignments:" << m_assignments << "default instances:" << m_instances;
}
static void reset() {
m_copies = 0;
m_assignments = 0;
m_instances = 0;
}
};
QAtomicInt Class::m_instances;
QAtomicInt Class::m_copies;
QAtomicInt Class::m_assignments;
typedef QVector<Class> Vector;
Q_DECLARE_METATYPE(Vector)
class Foo : public QObject
{
Q_OBJECT
Vector v;
public:
Foo() : v(100) {}
signals:
void containerSignal(const Vector &);
void classSignal(const Class &);
public slots:
void sendContainer() { emit containerSignal(v); }
void sendClass() { emit classSignal(Class()); }
};
class Bar : public QObject
{
Q_OBJECT
public:
Bar() {}
signals:
void containerDone();
void classDone();
public slots:
void containerSlotConst(const Vector &) {
Class::dump("Received signal w/const container");
}
void containerSlot(Vector v) {
Class::dump("Received signal w/copy of the container");
v[99] = Class();
Class::dump("Made a copy");
Class::reset();
Class::dump("Reset");
emit containerDone();
}
void classSlotConst(const Class &) {
Class::dump("Received signal w/const class");
}
void classSlot(Class) {
Class::dump("Received signal w/copy of the class");
emit classDone();
//QThread::currentThread()->quit();
}
};
int main(int argc, char ** argv)
{
QCoreApplication a(argc, argv);
qRegisterMetaType<Vector>("Vector");
qRegisterMetaType<Class>("Class");
Class::dump("Started");
QThread thread;
Foo foo;
Bar bar;
Class::dump("Created Foo");
bar.moveToThread(&thread);
Class::dump("Created Bar");
QObject::connect(&thread, SIGNAL(started()), &foo, SLOT(sendContainer()));
QObject::connect(&foo, SIGNAL(containerSignal(Vector)), &bar, SLOT(containerSlotConst(Vector)));
QObject::connect(&foo, SIGNAL(containerSignal(Vector)), &bar, SLOT(containerSlot(Vector)));
QObject::connect(&bar, SIGNAL(containerDone()), &foo, SLOT(sendClass()));
QObject::connect(&foo, SIGNAL(classSignal(Class)), &bar, SLOT(classSlotConst(Class)));
QObject::connect(&foo, SIGNAL(classSignal(Class)), &bar, SLOT(classSlot(Class)));
QObject::connect(&bar, SIGNAL(classDone()), &thread, SLOT(quit()));
QObject::connect(&thread, SIGNAL(finished()), &a, SLOT(quit()));
thread.start();
a.exec();
thread.wait();
}
#include "main.moc"