Должен ли я использовать shared_ptr или unique_ptr - PullRequest
49 голосов
/ 07 апреля 2011

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

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

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

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

Это правильно?

Ответы [ 4 ]

36 голосов
/ 07 апреля 2011

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

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

Pimpl - это не шаблон, а идиома, которая касается зависимости во время компиляции и двоичной совместимости. Он не должен влиять на семантику объектов, особенно в отношении их поведения при копировании.

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

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

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


Не имеет отношения к Pimpl

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

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

10 голосов
/ 07 апреля 2011

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

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

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

5 голосов
/ 07 апреля 2011

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

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

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

1 голос
/ 13 июня 2017

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

...