Удаление объектов отправляет сигналами, Владением объектов в сигналах, QT

Здесь, мое объявление сигнала:

signals:
    void mySignal(MyClass *);

И как я использую его:

MyClass *myObject=new myClass();
emit mySignal(myObject);

Здесь прибывает моя проблема: Кто ответственен за удаление myObject:

  1. Код отправителя, что, если это удаляет прежде, используется myObject? Висячий указатель

  2. Слот соединился с сигналом, что, если нет никакого слота или больше чем одного слота, который подключен к сигналу? Утечка памяти или Висячий указатель

Как спокойный управляет этой ситуацией в ее сборке - в сигналах? Это использует подсчет внутренней ссылки?

Каковы Ваши лучшие практики?

17
задан MikaS 25 January 2018 в 00:19
поделиться

4 ответа

Вы можете соединить сигнал со сколь угодно большим количеством слотов, поэтому вы должны убедиться, что ни один из этих слотов не может делать то, что вы не хотели бы, чтобы они делали с вашим объектом:

  • если вы решите передать указатель в качестве параметра, то вы столкнетесь с проблемами, которые вы описали, управление памятью - здесь никто не сможет сделать работу за вас, так как вам придется установить политику для работы с выделением/удалением. Некоторые идеи о том, как решить эту проблему, см. в Правилах управления памятью в COM мире.
  • если вы решите передать параметр в виде ссылки, то вам не придется беспокоиться об управлении памятью, а только о том, что слоты могут изменить ваш объект неожиданным образом. Идея заключается в том, чтобы не передавать указатели, если это не нужно - вместо этого используйте ссылки, если это возможно.
  • если вы решите передать const ссылку, то, в зависимости от типа вашего соединения, QT передаст значение объекта за вас (см. this для некоторых деталей)
  • избегайте любых проблем и передавайте по значению :)

Смотрите также question для некоторых мыслей о передаче указателей в сигналах.

10
ответ дан 30 November 2019 в 14:00
поделиться

В 1): Отправитель должен быть осторожен. При синхронной отправке сигнала (вместо очереди) объект все еще жив, когда его получает получатель. Если получателю нужно сохранить его, то поможет только QPointer, но тогда MyClass должен быть производным от QObject, что в контексте выглядит неправильно. В любом случае, это общий вопрос времени жизни, не очень специфичный для сигнала/слота.

Альтернативы: Использовать класс значений и передавать его через const-ссылку. Если MyClass может иметь подклассы, передавайте const QSharedPointer&

Насчет deleteLater: deleteLater() здесь не поможет. Она сделает соединения в очереди более безопасными, а для прямых соединений она не имеет никакого значения. Единственный случай, когда deleteLater() вступает в игру, это если получателю нужно удалить отправителя. Тогда всегда следует использовать deleteLater(), чтобы отправитель мог завершить то, что он делал, иначе произойдет сбой.

3
ответ дан 30 November 2019 в 14:00
поделиться

Для первого вопроса используйте QPointer

Для второго вопроса,

Если я правильно понял, даже если вы посылаете myObject, у вас все еще есть ссылка myObject в классе, в котором вы посылаете сигнал. Тогда как это будет утечка памяти или висячий указатель? Вы все еще можете получить доступ к myObject из излучаемого класса, не так ли?

Надеюсь, все ясно...

Edit :

Из ваших комментариев я полагаю, что вы освобождаете/удаляете объекты в слотах. Теперь я предполагаю, что ваша проблема в том, что если (освобождающий память) слот будет вызван один, два раза или не будет вызван вообще.

Для этого вы можете использовать QPointer. Из документации Qt:

Охраняемые указатели (QPointer) полезны, когда вам нужно хранить указатель на QObject, который принадлежит кому-то другому, и поэтому может быть уничтожен, пока вы все еще держите ссылку на него. Вы можете безопасно проверить указатель на достоверность.

Пример из самой документации Qt,

     QPointer<QLabel> label = new QLabel;
     label->setText("&Status:");
     ...
     if (label)
         label->show();

объяснение продолжается следующим образом...

Если в это время QLabel будет удален, переменная label будет содержать 0 вместо недопустимого адреса, и последняя строка никогда не будет выполнена. Здесь QLabel будет вашим MyClass, а label - вашим myObject. И перед использованием проверьте его на Nullity.

5
ответ дан 30 November 2019 в 14:00
поделиться

Одним словом (ладно, название функции) - deleteLater() :) Она есть у всех объектов QObjects. Она пометит объект для удаления, и это произойдет при следующем обновлении цикла событий.

0
ответ дан 30 November 2019 в 14:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: