Удаление объектов, отправленных по сигналам, Владение объектами в сигналах, Qt - PullRequest
17 голосов
/ 06 июля 2010

Вот мое объявление сигнала:

signals:
    void mySignal(MyClass *);

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

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

Вот моя проблема: Кто отвечает за удаление myObject:

  1. Код отправителя, что если он будет удален перед использованием myObject?Dangling Pointer

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

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

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

Ответы [ 4 ]

10 голосов
/ 06 июля 2010

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

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

См. Также этот вопрос , где можно найти мысли о прохождении указателей в сигналах.

5 голосов
/ 06 июля 2010

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

На второй вопрос

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

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

Редактировать:

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

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

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

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

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

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

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

3 голосов
/ 06 июля 2010

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

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

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

0 голосов
/ 06 июля 2010

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

...