Должен ли я использовать shared_ptr или unique_ptr
Я делаю некоторые объекты, используя идиому pimpl, но я не уверен, следует ли использовать std::shared_ptr
или std::unique_ptr
.
Я понимаю, что std::unique_ptr
более эффективен, но для меня это не столько проблема, как эти объекты относительно тяжеловесы, так что стоимость std::shared_ptr
over std::unique_ptr
относительно незначительна.
В настоящее время я работаю с std::shared_ptr
только из-за дополнительной гибкости. Например, использование std::shared_ptr
позволяет мне хранить эти объекты в хэш-карте для быстрого доступа, все еще имея возможность возвращать копии этих объектов вызывающим (как я считаю, любые итераторы или ссылки могут быстро стать недействительными).
Однако эти объекты в действительности не копируются, так как изменения влияют на все копии, поэтому мне было интересно, что, возможно, использование std::shared_ptr
и разрешение копий - это своего рода анти-шаблон или плохая вещь.
Правильно ли это?
Ответы
Ответ 1
Я делаю некоторые объекты, используя идиому pimpl, но я не уверен, использовать ли shared_ptr
или unique_ptr
.
Определенно unique_ptr
или scoped_ptr
.
Pimpl
- это не шаблон, а идиома, которая связана с зависимостью от компиляции и бинарной совместимостью. Это не должно влиять на семантику объектов, особенно в отношении его поведения при копировании.
Вы можете использовать любой тип интеллектуального указателя, который вы хотите использовать под капотом, но эти 2 гарантируют, что вы случайно не поделитесь реализацией между двумя отдельными объектами, поскольку они требуют сознательного решения о реализации конструктора копирования и назначения оператор.
Однако эти объекты в действительности не копируются, так как изменения влияют на все копии, поэтому мне было интересно, что, возможно, использование shared_ptr
и разрешение копий - это своего рода анти-шаблон или плохая вещь.
Это не анти-шаблон, на самом деле это шаблон: Aliasing. Вы уже используете его в С++ с общими указателями и ссылками. shared_ptr
предлагают дополнительную меру "безопасности", чтобы избежать мертвых ссылок за счет дополнительной сложности и новых проблем (остерегайтесь циклов, которые создают утечки памяти).
Не относится к Pimpl
Я понимаю, что unique_ptr
более эффективен, но для меня это не так много, так как эти объекты относительно тяжеловесы в любом случае, поэтому стоимость shared_ptr
over unique_ptr
относительно незначительна.
Если вы можете отмерить какое-то состояние, вы можете взглянуть на шаблон Flyweight.
Ответ 2
Если вы используете shared_ptr
, это не совсем классический pimpl
идиома (если вы не предпримете дополнительные шаги). Но реальный вопрос
почему вы хотите использовать интеллектуальный указатель для начала; это очень
ясно, где должно быть delete
, и нет проблем с
безопасности исключений или других, которые могут быть затронуты. В большинстве,
умный указатель сохранит вам строку или два кода. И
только тот, который имеет правильную семантику, равен boost::scoped_ptr
,
и я не думаю, что это работает в этом случае. (IIRC, это требует
полный тип для создания экземпляра, но я мог бы быть
неправильно.)
Важным аспектом идиомы pimpl является то, что его использование должно быть
прозрачный для клиента; класс должен вести себя точно так, как если бы
это было реализовано классически. Это означает либо ингибирование
копировать и присваивать или выполнять глубокую копию, если только класс
является неизменяемым (не несущественные функции-члены). Ни один обычный
интеллектуальные указатели выполняют глубокую копию; вы можете реализовать один, из
конечно, но, вероятно, все равно потребуется полный тип
всякий раз, когда происходит копия, а это означает, что вам все равно придется
предоставить пользовательский конструктор копирования и оператор присваивания
(поскольку они не могут быть встроенными). Учитывая это, это, вероятно, не
стоит того, чтобы использовать интеллектуальный указатель.
Исключением является то, что объекты неизменяемы. В этом случае это
не имеет значения, является ли копия глубокой или нет, и shared_ptr
полностью справляется с ситуацией.
Ответ 3
Когда вы используете shared_ptr
(например, в контейнере, затем просматриваете это и возвращаете его по-значению), вы не вызываете копию объекта, на который он указывает, просто копия указателя с счетчик ссылок.
Это означает, что если вы изменяете базовый объект из нескольких точек, вы влияете на изменения в одном экземпляре. Это именно то, для чего он предназначен, поэтому не какой-то анти-шаблон!
При передаче shared_ptr
(как следует из комментариев) лучше передать константу-ссылку и скопировать (там, увеличивая количество ссылок) там, где это необходимо. Что касается возврата, в каждом случае.
Ответ 4
Да, пожалуйста, используйте их. Проще говоря, shared_ptr - это реализация умного указателя. unique_ptr - это реализация автоматического указателя: