LINUX.ORG.RU

Глупый вопрос про удаление объектов

 


0

1

Недавно начал писать на Qt более или менее серьезное приложение. И сразу возникла проблема.

Есть у меня класс TcpLinkManager. Он содержит член QTcpSocket. В TcpLinkManager есть методы-слоты, которые реагируют на события connect/disconnect, идущие от QTcpSocket. И есть ещё один класс Log. Когда происходят события разрыва/установки соединения из класса TcpLinkManager вызываются методы Log, которые должны делать запись в лог. Экземпляры TcpLinkManager и Log сделаны ввиде глобальных переменных. И они видят друг-друга.

Когда я закрывю приложение, то наблюдается следующая картина:

  • Вызывается деструктор глобальной переменной Log::~Log(). Объект Log уничтожен.
  • Вызывается деструктор глобальной переменной TcpLinkManager::~TcpLinkManager(). Вызывается деструктор QTcpSocket. Он закрывает сокет и выбрасывает сигнал в метод-слот TcpLinkManager. Тот пытается вызвать метод объекта Log для записи сообщения. Но объект Log уже уничтожен.
  • Segmentation fault....

Раньше я не сталкивался с подобными случаями. И не задумывался о таких вещах. Привык, что деструкторы уничтожают объект и не вызывают кучу сторонних методов с неожиданными сайд-эффектами. Я решил проблему добавлением в код TcpLinkManager::~TcpLinkManager() вызова метода .blockSignals(true) для экземпляра QTcpSocket. Но такое решение мне не нравится. Хочется так писать код, чтобы не задумываться о том, в какой последовательности будут уничтожаться объекты.

Вопрос: Какой есть способ «красиво» решить проблему? Как пишут те, кто много работает с Qt?

эм... уничтожать Log после TcpLinkManager ?

Megamozg
()

Тот пытается вызвать метод объекта Log для записи сообщения. Но объект Log уже уничтожен.

Делать проверку на то, существует ли Log?

anonymous
()

Какой есть способ «красиво» решить проблему?

Экземпляры TcpLinkManager и Log сделаны ввиде глобальных переменных

глобальных переменных

я думаю ты догадался, в какую сторону копать?

nanoolinux ★★★★
()

сделать правильный дизайн, без глобальных переменных, удалять объекты последовательно

trashymichael ★★★
()

QObject::disconnect(), не?

shty ★★★★★
()

и не надо делать глобальные переменные, это ад и израиль

shty ★★★★★
()
Последнее исправление: shty (всего исправлений: 1)
Ответ на: комментарий от anonymous

Если бодробнее то Андрей Александреску, «Современное проектирование на C++» Глава 6. Реализация шаблона Singleton. Там рассматривается именно ваша проблема.

anonymous
()

Когда уничтожаешь глобальный Log:

delete LogInstance;
LogInstance = NULL;

В коде записи лога

if(LogInstance!=NULL)
{
    ...
}

Но вообще глобальные объекты это плохо.

trex6 ★★★★★
()

Не используй глобальные переменные. В дополнение к их общей ненужности, они еще и уничтожаются в неопределенном порядке, если расположены в разных файлах.

Pavval ★★★★★
()

Делаешь управленца сервисами, который предоставляет глобальные объекты. Инициализируешь и уничтожаешь его в main, для всех остальных он «глобальный», он же предоставляет тебе другие глобальные объекты, он их сам создает и сам уничтожает в нужном порядке.

anonymous
()
Ответ на: комментарий от nanoolinux

я думаю ты догадался, в какую сторону копать?

Да что вы все повторяете эту мантру. Если ты внимательно посмотришь, то поймешь, что дело не в глобальных переменных. Можно привести пример, где глобальные переменные отсутствуют, а ситуация полностью аналогична.

pathfinder ★★★★
() автор топика
Ответ на: комментарий от Deleted

Если с++11, то можно nullptr.
А если старый стандарт, то что тогда может NULL заменить?
И почему не стоит использовать?

trex6 ★★★★★
()
Ответ на: комментарий от pathfinder

Если объекты не глобальные, то можно точнее управлять их временем жизни. У тебя TcpLinkManager явно зависит от Log. Следовательно Log не должен уничтожаться раньше, чем TcpLinkManager.

trex6 ★★★★★
()
Ответ на: комментарий от trex6

Нулём, не?

>И почему не стоит использовать?
В C++ дважды не Ъ, сам по факту и по факту бытия макросом.

Deleted
()
Ответ на: комментарий от trex6

Если объекты не глобальные, то можно точнее управлять их временем жизни. У тебя TcpLinkManager явно зависит от Log. Следовательно Log не должен уничтожаться раньше, чем TcpLinkManager.

То, что ты говоришь разумно. Но я бы хотел исходить из равнозначности объектов в плане их создания и уничтожения. Ведь не всегда можно сразу сказать, что объект А должен быть инициализирован раньше, а уничтожен позже чем объекты B и C. То есть необходимый порядок их создания не вытекает строгим образ из функциональности и логики работы объектов.

pathfinder ★★★★
() автор топика
Ответ на: комментарий от nanoolinux

попробуй QObject::deleteLater

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

pathfinder ★★★★
() автор топика

Я пока склоняюсь к идее двух-этапной инициализации, когда помимо конструктора/деструктора есть ещё пара методов Init()/Close(). Close() должен сделать объект «пассивным», то есть таким, чтобы при уничтожении не порождал сторонних эффектов на другие объекты.

Думаю для TcpLinkManager в методе Close() достаточно будет закрыть сокет и заблокировать повторную установку соединения.

pathfinder ★★★★
() автор топика
Ответ на: комментарий от pathfinder

Ведь не всегда можно сразу сказать, что объект А должен быть инициализирован раньше, а уничтожен позже чем объекты B и C.

Такие вещи, на мой взгляд, относяться к явным проблемам архитектуры.

trex6 ★★★★★
()
Ответ на: комментарий от Deleted

Зато NULL легко заменить на nullptr, а вот с 0 это так просто не прокатит.

trex6 ★★★★★
()
Ответ на: комментарий от anonymous

Вообще можно, но на мой взгляд код

if(ptr!=NULL)

более читабелен.

trex6 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.