Должен ли я использовать shared_ptr или unique_ptr

Я делаю некоторые объекты, используя идиому pimpl, но я не уверен, следует ли использовать std::shared_ptr или std::unique_ptr .

Я понимаю, что std::unique_ptr более эффективен, но для меня это не столько проблема, сколько эти объекты относительно тяжеловесы, так что стоимость std::shared_ptr над std::unique_ptr относительно небольшая.

В настоящее время я использую std::shared_ptr только из-за дополнительной гибкости. Например, использование std::shared_ptr позволяет мне хранить эти объекты в hash-карте для быстрого доступа, все еще имея возможность возвращать копии этих объектов вызывающим абонентам (как я считаю, любые iteratorы или ссылки могут быстро стать недействительными).

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

Это верно?

Я делаю некоторые объекты, используя идиому pimpl, но я не уверен, использовать ли shared_ptr или unique_ptr .

Определенно scoped_ptr или scoped_ptr .

Pimpl – это не шаблон, а идиома, которая связана с компиляционной зависимостью и бинарной совместимостью. Это не должно влиять на семантику объектов, особенно в отношении его поведения при копировании.

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

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

Это не анти-шаблон, на самом деле это шаблон: слияние. Вы уже используете его в C ++ с общими указателями и ссылками. shared_ptr предлагает дополнительную меру «безопасности», чтобы избежать мертвых ссылок за счет дополнительной сложности и новых проблем (остерегайтесь циклов, которые создают утечки памяти).


Не относится к Pimpl

Я понимаю, что unique_ptr более эффективен, но для меня это не столько проблема, как все эти объекты относительно тяжеловесы, так что стоимость shared_ptr над unique_ptr относительно незначительна.

Если вы можете отбросить какое-то состояние, вы можете взглянуть на шаблон Flyweight .

Если вы используете shared_ptr , это не classическая идиома pimpl (если вы не предпримете дополнительные шаги). Но реальный вопрос заключается в том, почему вы хотите использовать умный указатель для начала; очень ясно, где должно произойти delete , и нет проблем с безопасностью исключений или другими, которые могут быть затронуты. В лучшем случае смарт-указатель сохранит вам строку или два кода. И единственный, у которого есть правильная семантика, – boost::scoped_ptr , и я не думаю, что это работает в этом случае. (IIRC, для создания экземпляра требуется полный тип, но я могу ошибаться).

Важным аспектом идиомы pimpl является то, что ее использование должно быть прозрачным для клиента; class должен вести себя точно так, как если бы он выполнялся classически. Это означает либо запрещение копирования, либо присвоение или реализацию глубокой копии, если только class не является неизменным (нет функций, не являющихся константными членами). Ни один из обычных интеллектуальных указателей не выполняет глубокую копию; вы, конечно, могли бы реализовать один, но, вероятно, все равно потребуется полный тип всякий раз, когда произойдет копия, а это означает, что вам все равно придется предоставить определенный пользователем конструктор копирования и оператор присваивания (поскольку они не могут быть встроенными). Учитывая это, вероятно, не стоит беспокоиться об использовании умного указателя.

Исключение – если объекты неизменяемы. В этом случае не имеет значения, является ли копия глубокой или нет, а shared_ptr обрабатывает ситуацию.

Когда вы используете shared_ptr (например, в контейнере, затем просматриваете это и возвращаете его по значению ), вы не вызываете копию объекта, на который указывает, просто копия указателя с подсчетом ссылок.

Это означает, что если вы изменяете базовый объект из нескольких точек, вы влияете на изменения в одном экземпляре . Это именно то, для чего он предназначен, поэтому не какой то анти-шаблон !

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

Да, пожалуйста, используйте их. Проще говоря, shared_ptr – это реализация умного указателя. unique_ptr – это реализация автоматического указателя:

  • Форвардная декларация с уникальным_ptr?
  • Поиск необработанного указателя для наборов unique_ptrs
  • Как захватить unique_ptr в выражение lambda?
  • Различия между unique_ptr и shared_ptr
  • Возврат unique_ptr из функций
  • make_unique и совершенная переадресация
  • Является ли std :: unique_ptr необходимым для определения полного определения T?
  • Как передать аргумент unique_ptr конструктору или функции?
  • Вызов конструктора initializer_list через make_unique / make_shared
  • Давайте будем гением компьютера.