ОБНОВЛЕНИЕ 20-АПРЕЛЯ 2015
Первоначально Я полагал, что передача ссылки на объект, выделенный из стека, будет эквивалентна передаче адреса этого объекта. Следовательно, в отсутствие оболочки, в которой будет храниться копия (или общий указатель) , соединение в слот с очередями может завершиться с использованием неверных данных.
Но @BenjaminT и @cgmb обратили мое внимание на то, что Qt действительно имеет специальную обработку для константных ссылочных параметров. Он вызовет конструктор копирования и уберет скопированный объект для использования в вызовах слотов. Даже если исходный объект, который вы передали, был уничтожен к моменту запуска слота, ссылки, которые получат слоты, будут полностью относиться к разным объектам.
Вы можете прочитать @ cgmb's ответ для получения подробных сведений о механике. Но вот быстрый тест:
#include <iostream>
#include <QCoreApplication>
#include <QDebug>
#include <QTimer>
class Param {
public:
Param () {}
Param (Param const &) {
std::cout << "Calling Copy Constructor\n";
}
};
class Test : public QObject {
Q_OBJECT
public:
Test () {
for (int index = 0; index < 3; index++)
connect(this, &Test::transmit, this, &Test::receive,
Qt::QueuedConnection);
}
void run() {
Param p;
std::cout << "transmitting with " << &p << " as parameter\n";
emit transmit(p);
QTimer::singleShot(200, qApp, &QCoreApplication::quit);
}
signals:
void transmit(Param const & p);
public slots:
void receive(Param const & p) {
std::cout << "receive called with " << &p << " as parameter\n";
}
};
... и основной:
#include <QCoreApplication>
#include <QTimer>
#include "param.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// name "Param" must match type name for references to work (?)
qRegisterMetaType<Param>("Param");
Test t;
QTimer::singleShot(200, qApp, QCoreApplication::quit);
return a.exec();
}
Выполнение этого демонстрирует, что для каждого из 3-х слотовых соединений отдельная копия Param создается через конструктор копирования:
Calling Copy Constructor
Calling Copy Constructor
Calling Copy Constructor
receive called with 0x1bbf7c0 as parameter
receive called with 0x1bbf8a0 as parameter
receive called with 0x1bbfa00 as parameter
Вы можете задаться вопросом, что хорошего в том, чтобы "передавать по ссылке", если Qt все равно собирается делать копии. Однако, это не всегда делает копию ... это зависит от типа соединения. Если вы измените на Qt::DirectConnection
, он не сделает никаких копий:
transmitting with 0x7ffebf241147 as parameter
receive called with 0x7ffebf241147 as parameter
receive called with 0x7ffebf241147 as parameter
receive called with 0x7ffebf241147 as parameter
И если вы переключитесь на , передавая значение , вы получите более промежуточные копии, особенно в случае Qt::QueuedConnection
:
Calling Copy Constructor
Calling Copy Constructor
Calling Copy Constructor
Calling Copy Constructor
Calling Copy Constructor
receive called with 0x7fff15146ecf as parameter
Calling Copy Constructor
receive called with 0x7fff15146ecf as parameter
Calling Copy Constructor
receive called with 0x7fff15146ecf as parameter
Но проход по указателю не создает особой магии. Так что у него есть проблемы, упомянутые в оригинальном ответе, который я буду держать ниже Но оказалось, что обработка ссылок - это просто другой зверь.
ОРИГИНАЛЬНЫЙ ОТВЕТ
Да, это может быть опасно, если ваша программа многопоточная. И это вообще плохой стиль, даже если нет. На самом деле вы должны передавать объекты по значению через сигнальные и слот-соединения.
Обратите внимание, что Qt поддерживает «неявно разделяемые типы», поэтому передача таких вещей, как QImage «по значению», не сделает копию, если кто-то не запишет полученное значение:
http://qt -project.org / док / кварта-5 / неявная sharing.html
Проблема принципиально не связана с сигналами и слотами. В C ++ есть разные способы удаления объектов, когда на них есть ссылки, или даже если какой-то их код выполняется в стеке вызовов. Вы можете легко попасть в эту проблему в любом коде, где у вас нет контроля над кодом и используется правильная синхронизация. Такие методы, как использование QSharedPointer, могут помочь.
Есть несколько дополнительных полезных вещей, которые Qt предлагает для более изящной обработки сценариев удаления. Если есть объект, который вы хотите уничтожить, но вы знаете, что он может использоваться в данный момент, вы можете использовать метод QObject :: deleteLater ():
http://qt -project.org / док / кварта-5 / qobject.html # deleteLater
Это пригодилось мне пару раз. Еще одна полезная вещь - сигнал QObject :: destroy ():
http://qt -project.org / док / кварты-5 / qobject.html # уничтожены