Здравствуйте, m2user, Вы писали:
SaZ>>Там уже почти на все вопросы ответили. Добавлю про объекты на стеке. Если вы объекту на стеке сделаете setParent и удалите его нового родителя, то будет как минимум двойной вызов деструктора со всеми вытекающими последствиями. А так, в целом, всё будет ок.
M>В иерархии parent-child я пока не углублялся, но буду иметь в виду.
M>В контексте слотов/сигналов получается, что объекты на стеке (локальные переменные функции или метода) вообще применять не следует, если создается connection c объектом, у которого время жизни более длительное.
M>И как тогда быть с RAII?
Это разные вещи. В контексте сигналов-слотов тут всё ок, за исключением случая, если вы кинете сигнал из объекта на стеке в другой поток, и в слоте из другого потока попытаетесь взять sender(). Аргументы будут передаваться всегда через копию, даже если сигнатура с ссылками. Там будет или nullptr или невалидный указатель. Я именно про управление памятью говорил, советую почитать:
https://doc.qt.io/qt-6/objecttrees.html
M>Вот тут (https://forum.qt.io/post/776397) предлагают QSharedPointer с deleteLater в качестве deleter. Насколько это годной подход?
Ну, как правило, такое не нужно, но если осторожно использовать, то можно. Если у вас будет какой-то parent (неважно как созданный) и вы ему докинете child который лежит в таком шаредпоинтере, то при удалении парента у вас будет шаред поинтер с удалённым объектом.
В целом правило простое — если вы для управления памятью используете иерархию QObject, то либо сырые указатели на объекты в куче, либо QPointer.
Во всех остальных случаях (стек, смартпоинтеры) — никаких вызовов setParent / moveToThread.
SaZ>>И если в слоте вы дёрнете sender() для объекта который помечен через deleteLater или находится в состоянии удаления, то qobject_cast<ВашТип> вернёт nullptr. Мне как-то раз понадобилось определять, удаляется ли сейчас объект или нет и я нашёл такой вот способ.
M>Но ведь этот способ ограничено применим: объект может быть не в состоянии удаления на момент проверки, но стать таковым после нее.
Да, и если программист берёт на себя ответственность, то делается обычный static_cast, но это как правило стрельба у себя между ног, с надеждой что не зацепит.
M>Возможно я не совсем правильно понимаю Warning из документации:
M>M>Warning: Deleting a QObject while pending events are waiting to be delivered can cause a crash. You must not delete the QObject directly if it exists in a different thread than the one currently executing. Use deleteLater() instead, which will cause the event loop to delete the object after all pending events have been delivered to it.
M>В ней говорится про delete объекта принимающего события.
M>Является ли delete объекта генерирующего события проблемным и если да, то в какой степени (cause a crash и т.д)
Советую всё-таки почитать документацию кутэ по фундаментальным вопросам. Сэкономите кучу времени потом =). Для начала:
https://doc.qt.io/qt-6/threads-qobject.html
В кутэ есть понятия принадлежности объекта к потоку. Каждый QObject под капотом хранит указатель на QThread где крутится QEventLoop и будут вызываться фильтры событий и обработчики слотов (если явно не указан DirectConnection).
Но ворнинг вполне логичный — не нужно удалять то, что ещё может быть где-то вызвано. Если у вас слот вызывается в другом потоке, то в нём же, в самом конце, сделайте sender()->deleteLater(); и ваш объект корректно уничтожится в том потоке, где живёт.
SaZ>>Ещё один неочевидный нюанс — это принадлежность к потокам. Если у вас объекты в общей иерархии памяти (родитель-ребёнок), то они всегда будут принадлежать одному потоку. И поменять принадлежность для всей иерахии можно только через корневой элемент. И да, делать moveToThread для объекта на стеке — тоже большой риск выстрелить себе в ногу. Надо очень хорошо будет следить за временем жизни объектов.
M>Про thread affinity.
M>Из документации не совсем ясно, почему выбран подход с привязкой объекта именно к потоку выполнения.
Выше написал

, тут в основном про то, где дёргать слоты через QueuedConnection (и подобные).
M>Иными словами я могу представить, что объект не является thread safe, т.е. обращение к нему должно быть только последовательными (сериализованными). И должна быть некая очередь вызовов.
M>Одна на объект или группу объектов. По идее эти вызовы могут выполняться на произвольном потоке (например из некоего thread pool`а).
В общем случае нет. Читайте:
https://doc.qt.io/qt-6/threads-reentrancy.html