Должен ли я использовать unique_ptr или shared_ptr в этом случае?

в главном окне моего приложения QT я использую std::shared_ptr для хранения указателя на экземпляр моей сетевой службы, который управляет всеми подключениями к нескольким клиентам. Теперь я должен передать этот указатель нескольким вспомогательным окнам, чтобы они могли общаться с клиентами.

Лучше ли использовать переменную-член std::shared_ptr в основном и под-окнах и передавать ее при создании подокнов или лучше использовать std::unique_ptr и передать необработанный указатель на под Windows, как mainwindow будет переживать subwindows в любом случае?

Спасибо большое!

Ответ 1

Основное практическое различие заключается в том, что происходит, когда mainwindow уничтожается, пока подпоток все еще существует и использует сетевую услугу:

  • Если вы используете unique_ptr и передаете исходные указатели, вы получаете поведение undefined.
  • Если вы используете shared_ptr, то сетевой сервис остается до тех пор, пока все подокна не будут уничтожены.

Теперь, если это условие невозможно по дизайну, поведение undefined по своей сути не является проблемой. Если условие происходит в любом случае из-за ошибки, это может помочь вам обнаружить ошибку, если сетевая служба будет уничтожена вместе с основным окном, что произойдет, если вы используете unique_ptr. Использование unique_ptr выражает, что mainwindow - единственное, что принадлежит сетевому сервису, другие просто используют его по указанию mainwindow.

С другой стороны, если вы позже измените дизайн и хотите, чтобы это условие было законным, или если вы хотите использовать подзоны по-другому, что означает, что нет единого объекта сетевой службы, который они все используют, и который их переживает все, тогда будет проще, если вы использовали shared_ptr с самого начала. Использование shared_ptr выражает, что все окна имеют право владеть сетевым сервисом.

Я не думаю, что вообще можно сказать, следует ли пытаться заставить ваш код продолжать работать перед лицом "невозможных условий". В этом случае очень дешево это сделать (shared_ptr дороже копировать, чем исходный указатель, конечно, но дешево по сравнению с созданием подзаголовка, а код структурирован одинаково в любом случае). Может быть полезно иметь гибкость, чтобы сделать "невозможное условие" возможным при определенных обстоятельствах, например, когда модуль тестирует код подкачки. Поэтому, возможно, используйте shared_ptr для гибкости. Если принудительное ограничение жизненного цикла является большой проблемой по другим причинам, тогда вам может не понадобиться гибкость, и в этом случае избегайте писать код, который будет только скрывать ошибки.

Ответ 2

Собственно, есть 3-я альтернатива, которую вы не изучили.

  • Передача shared_ptr позволяет вам иметь несколько владельцев, однако, кажется, в вашем случае это не нужно.
  • Использование unique_ptr и передача необработанного указателя накладывает действие на одного владельца, но в случае ошибки вы столкнулись с поведением undefined (сбой).

Третий вариант заключается в объединении обоих подходов с помощью weak_ptr:

  • Главное окно принадлежит устройству в shared_ptr
  • Под окнами передаются a weak_ptr

Когда вспомогательное окно должно взаимодействовать с использованием устройства, они будут использовать lock() на weak_ptr, чтобы временно получить shared_ptr. Затем вы можете утверждать или бросать, если shared_ptr пуст (это ошибка), и в противном случае использовать его для связи с устройством, а затем разрешить его, как вы это сделали.


EDIT: поскольку Стив Джессоп заметил, что другое утверждение полезно (и достижимо): гарантируя, что, когда главное окно разрушает shared_ptr, это был последний владелец и устройство было выпущено (чтобы защитить случайная утечка владения).

Наивный способ, утверждающий, что он unique до разрушения, к сожалению, страдает от состояния расы; между вызовом unique и фактическим уничтожением вызов weak_ptr::lock может создать нового владельца.

Это можно сделать просто:

  • Создайте новый weak_ptr под названием monitor из shared_ptr.
  • Reset shared_ptr.
  • Вызов monitor.lock(), если он возвращает ненулевой shared_ptr, тогда есть владелец в дикой природе.

Ответ 3

Вопрос об использовании std::shared_ptr или std::unique_ptr в основном является собственностью. Будет ли только один владелец одновременно к объекту? Или будет много владельцев объекта?

В вашем случае кажется, что вы хотите нескольких владельцев, поэтому std::shared_ptr - это путь.

Не используйте std::unique_ptr, а затем обходите необработанный обернутый указатель. Он вернется, чтобы укусить вас, когда вы его забудете, а объект std::unique_ptr выходит за рамки, в то время как у кого-то еще есть доступ к необработанному указателю.

Ответ 4

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

О единственном случае, когда вам нужно использовать указатели, является тип является полиморфным, а фактический тип неизвестен до (например, поскольку он определяется вхождениях в файл конфигурации). В этом случае вам придется управлять жизни, но если это соответствует сфере действия, std::unique_ptr может быть хорошим решением, поскольку, если он не перемещен, он точно эмулирует время жизни переменной с областью. (Я бы будьте очень скептически относитесь к std::shared_ptr здесь; срок службы должен вероятно, будет детерминированным и не зависит от того, кто-то происходит, чтобы удерживать указатель на него. Я также могу представить сценарии где сетевой сервер будет содержать указатели на своих клиентов, с риском циклов.)