Nie ma znaczenia, czy pętle zdarzeń działają. Gdy masz emit
sygnał z QObject
, który żyje w wątku innym niż obiekt gniazda, sygnał zostanie wysłany jako QMetaCallEvent
do kolejki zdarzeń odbieranego wątku. Pętla zdarzeń działająca w odbierającym wątku będzie działała na to zdarzenie i wykona wywołanie w gnieździe, które zostało podłączone do emitowanego sygnału.
Tak więc, niezależnie od tego, co się stanie, jakiekolwiek dane, które wyślesz przez sygnał, ostatecznie staną się ładunkiem w instancji klasy pochodnej QEvent.
Mięsem problemu jest moment, w którym QMetaCallEvent
osiągnie pętlę zdarzeń, a pojemnik zostanie przekazany do gniazda jako argument. Oczywiście konstruktorzy kopii mogliby się nazywać wiele razy po drodze.Poniżej jest jakiś prosty kod, który pokazuje, ile razy konstruktor kopiujący i domyślne konstruktora są w rzeczywistości nazywa
na elementach członków danych o niejawnie wspólnego pojemnika copy-on-write (QVector)
na niestandardowej klasie, która zastępuje kontener.
Będziesz mile zaskoczony :)
Ponieważ kontenery Qt są niejawnie udostępniane kopiowanie przy zapisie, ich budowa egzemplarz ma znikomy koszt: wszystko, co się stało, to licznik jest zwiększany odniesienia atomowo na budowie . Na przykład żaden z członków danych nie jest kopiowany.
Niestety, przed 11 C++ pokazuje swoją paskudną stronę: jeśli kod slotu modyfikuje kontener w jakikolwiek sposób, nie ma możliwości przekazania referencji do gniazda w taki sposób, aby kompilator wiedział, że oryginalny pojemnik jest już nie potrzebne. Tak więc: jeśli slot otrzymuje stałe odniesienie do kontenera, masz gwarancję, że nie zostaną wykonane żadne kopie. Jeśli slot otrzyma możliwą do zapisania kopię kontenera i, zostanie zmodyfikowana, zostanie wykonana całkowicie niepotrzebna kopia, ponieważ instancja żywa na stronie wywołania nie jest już potrzebna. W C++ - 11 przekazałbyś wartość rurnu jako parametr. Przekazywanie referencji rvalue w wywołaniu funkcji kończy czas życia przekazanego obiektu w wywołującym.
Przykładowy kod wyjściowy:
"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"
Musimy wiedzieć więcej. Czy wątek główny może zostać utracony? Co tak czy owak robi główny wątek z danymi? Ale bez względu na twoje potrzeby, nie mogę uwierzyć, że emitowanie tablicy jest optymalnym rozwiązaniem. – TonyK