Russian Qt Forum

Qt => Общие вопросы => Тема начата: DS_tm от Ноябрь 13, 2010, 23:46



Название: Удаление указателя внутри слота
Отправлено: DS_tm от Ноябрь 13, 2010, 23:46
Есть сигнал содержащий в списке параметров указатель на объект (не QObject). Есть несколько не связанных между собой объектов обрабатывающих в своих слотах данный сигнал. Один из слотов использует оператор delete для очистки памяти на которую указывает указатель (сорри за тавтологию). В результате в других слотах происходит ошибка обращения к удаленной памяти. Как можно решить данную проблему?


Название: Re: Удаление указателя внутри слота
Отправлено: vlad-mal от Ноябрь 14, 2010, 05:39
Нормально.

А какое поведение вы бы хотели?

Чтобы не обращаться к удаленному объекту?
Чтобы каждый слот работал со своим экземпляром объекта?
...
?

Цитировать
- Доктор, мне больно когда я так делаю!
- Ну не делайте так!


Название: Re: Удаление указателя внутри слота
Отправлено: DS_tm от Ноябрь 14, 2010, 13:41
Да, я понимаю, что поведение ожидаемое, вопрос в том, возможно ли обойти его.
На вскидку вижу 3 решения проблемы.
1) В каждом слоте проверять, указывает указатель на что-то валидное или на нойс (в рамках С++ я не знаю как это сделать)
2) Заглушить емит сигнала к другим слотам. (самый адекватный вариант на мой взгляд, но в Qt ничего подобного вроде нет)
3) Удостовериться, что слот, удаляющий объект запускается последним (тоже нет механизмов регулирования данной последовательности. Мое предположение, что используется стек (LIFO), то есть чтобы слот обрабатывался последним его нужно приконектить первым, но это очень нестабильный вариант).

в итоге 0 готовых решений)


Название: Re: Удаление указателя внутри слота
Отправлено: BlackTass от Ноябрь 14, 2010, 13:53
сделать его наследником QObject и вызвать deleteLater()?


Название: Re: Удаление указателя внутри слота
Отправлено: lit-uriy от Ноябрь 14, 2010, 14:12
DS_tm, обычная практика:
перед обращением к объекту через указатель, делать проверку на нуль:
Код
C++ (Qt)
if (pointer) pointer->method();

сразу после удаления объекта обнулять указатель:
Код
C++ (Qt)
delete pointer;
pointer = 0;


Название: Re: Удаление указателя внутри слота
Отправлено: DS_tm от Ноябрь 14, 2010, 14:14
сделать его наследником QObject и вызвать deleteLater()?

К сожалению не вариант, класс из 3th-part библиотеки. В итоге делаю воркераунд вроде того, что вы предложили, коллекционирую
указатели в список и удаляю потом. Удаление происходит по таймауту (время заведомо большее, чем потребуется для обработки всех слотов) либо в деструкторе класса, содержащего данный слот. Оба варианта мне не очень нравятся, думаю переписать с использованием postEvent() и своим евентом, который вызывает очистку списка (собственно deleteLater() так и работает).


Название: Re: Удаление указателя внутри слота
Отправлено: DS_tm от Ноябрь 14, 2010, 14:16
DS_tm, обычная практика:
перед обращением к объекту через указатель, делать проверку на нуль:
Код
C++ (Qt)
if (pointer) pointer->method();

сразу после удаления объекта обнулять указатель:
Код
C++ (Qt)
delete pointer;
pointer = 0;

Этот вариант попробовал сразу, но, как я и думал, результата нет. Причина в том что при emit используется копия указателя для каждого слота и обнуление одной копии ничего не дает для другой.


Название: Re: Удаление указателя внутри слота
Отправлено: lit-uriy от Ноябрь 14, 2010, 15:13
покажи код, как посылаешь сигнал и как принимаешь в слоте


Название: Re: Удаление указателя внутри слота
Отправлено: vlad-mal от Ноябрь 14, 2010, 15:20
DS_tm, вы так и не описали, что вы хотите:

Вариант 1. При удалении объекта в одном из слотов хотите знать, что объект удален, и не обрабатывать его?

Вариант 2. В каждом из слотов работать с объектом, а удалять по окончании обработки во всех слотах?

~~~~~~~~~~~~~~~~~~~~~~

Можете в классе объекта (или его "обертке") завести статическое поле - счетчик ссылок.

Для варианта 2:
Если нужен второй вариант, то установите его в значение, равное числу слотов, а потом отправляете объект.
В каждом слоте, после использования, перед разрушением объекта уменьшаем счетчик на 1, и если он стал равен нулю - смело удаляем.

Для варианта 1 счетчик устанавливаем в 1.
В слотах перед использованием проверяем, если не ноль - уменьшаем на 1 и используем по полной программе, потом удаляем.
Не забываем о синхронизации.


Название: Re: Удаление указателя внутри слота
Отправлено: vlad-mal от Ноябрь 14, 2010, 15:21
сделать его наследником QObject и вызвать deleteLater()?

К сожалению не вариант, класс из 3th-part библиотеки...
...
Ну так "оберните" этот объект в свой!


Название: Re: Удаление указателя внутри слота
Отправлено: DS_tm от Ноябрь 14, 2010, 19:34
покажи код, как посылаешь сигнал и как принимаешь в слоте

Что-то типа:

Код
C++ (Qt)
class NotQObject
{
...
public:
 void someFunct();
}
 
class emitClass : public QObject
{
...
 signals:
   void mySignal(NotQObject *obj);
}
 
class slotClass1 : public QObject
{
...
public slots:
 void slotMySignal(NotQObject *obj)
 {
   ...
   obj->someFunct();
   ...
 }
}
 
class slotClass2 : public QObject
{
...
public slots:
 void slotMySignal(NotQObject *obj)
 {
   ...
   delete obj;
   ...
 }
}
 

Ну и собственно слоты slotMySignal обоих классов где-то подключены к сигналу mySignal.


Название: Re: Удаление указателя внутри слота
Отправлено: DS_tm от Ноябрь 14, 2010, 19:50
DS_tm, вы так и не описали, что вы хотите:
Вариант 1. При удалении объекта в одном из слотов хотите знать, что объект удален, и не обрабатывать его?
Вариант 2. В каждом из слотов работать с объектом, а удалять по окончании обработки во всех слотах?
<...>

Спасибо, меня устроили оба варианта, Ваши идеи по поводу счетчика ссылок интересны. Правда помимо создания врапера для класса объекта понадобиться еще делать врапер для класса посылающего сигнал (он тоже из внешней библиотеки, а следовательно инерфейс сигнала зафиксирован). Еще мне непонятно зачем Вы предложили использовать статический член, если объектов много то они все будут оперировать одним счетчиком ссылок, что в конечном итоге приведет к суматохе. Не могу не заметить также, что первый вариант тогда не доступен, ибо если делать член нестатичным то, при удалении объекта, к нему уже никак не обратиться. Во-втором же варианте есть небольшая проблема, связанная с необходимостью точно знать, сколько слотов подключено к сигналу, что в принципе не очень то удобно, нужно всегда помнить о счетчике при добавлении/удалении слота для сигнала.
В итоге я думаю все же остановиться на использовании postEvent() функции и очистке списка объектов после обработки всех сигналов. Плюс не надо создавать два класса врапера.


Название: Re: Удаление указателя внутри слота
Отправлено: vlad-mal от Ноябрь 14, 2010, 19:54
Ну, не угадал. :)
Обычное дело, когда вопрос задан в форме: "кто первый догадается, что мне нужно?"


Название: Re: Удаление указателя внутри слота
Отправлено: DS_tm от Ноябрь 15, 2010, 12:03
Ну, не угадал. :)
Обычное дело, когда вопрос задан в форме: "кто первый догадается, что мне нужно?"
Я просто не вижу смысла особого задавать конкретный вопрос "как сделать что-то, если входные данные такие, а на выходе надо получить это?". Чаще всего для таких вопросов достаточно внимательно посмотреть маны ну или погуглить пару минут.
А тут небольшой брейнстормчик, взгляд на проблему со стороны, все дела. В итоге агрегируя идеи получается нормальное готовое решение. Спасибо большое за участие, приходите еще!  ;)