Некоторые вопросы о shared_ptr, scoped_ptr и shared_array - PullRequest
1 голос
/ 03 апреля 2012

У меня есть несколько вопросов о интеллектуальных указателях, реализованных в буст-библиотеке.Является ли единственное различие между shared_ptr и scoped_ptr в том, что scoped_ptr не имеет конструктора копирования, а shared_ptr имеет его?Должен ли я использовать тогда scoped_ptr вместо shared_ptr всегда, когда объект не вызывает конструктор копирования?Я также не понимаю идею общего / объемного массива.Разве я не могу использовать вместо него std :: vector?

Ответы [ 5 ]

10 голосов
/ 03 апреля 2012

Является единственным различием между shared_ptr и scoped_ptr, которое scoped_ptr не имеет конструктора копирования, а shared_ptr имеет его?

Разница более фундаментальная, чем эта; это связано с тем, как умные указатели владеют объектом, на который он указывает. Что отличает умные указатели от тупых указателей, так это то, что концепция владения является ключевым компонентом их функций. Семантика владения - это то, что отличает разные виды умных указателей.

Поскольку умные указатели «владеют» тем, на что они указывают, они могут делать полезные вещи, такие как удаление объектов, когда умные указатели исчезают (это возможно благодаря использованию только правил языка; не требуется никакой магии компилятора). Таким образом, управление памятью в C ++ может быть почти автоматическим (несмотря на все заявления об обратном, в современном C ++ очень мало ручного управления памятью).

  • shared_ptr реализует подсчет ссылок семантика для управление памятью. Несколько shared_ptr s могут владеть одним объектом. shared_ptr уход не обязательно удаляет объект указывает на то, что может быть другой shared_ptr, владеющий объект. Последний shared_ptr, который владеет объектом и уходит, будет удалить принадлежащий ему объект.

  • scoped_ptr реализует семантику исключительной собственности. Только один scoped_ptr может владеть любым одним объектом. Когда scoped_ptr уходит, он всегда будет удалять принадлежащий ему объект (потому что есть только один владелец). Обычно он используется как легкий механизм RAII для объекты, размещенные в свободном магазине.

Версии массива (shared_array и scoped_array) имеют практически одинаковую семантику, но предназначены специально для массивов, например они используют delete[] вместо delete, реализуют оператор индекса массива и т. д.

shared_ptr и shared_array также позволяют указать пользовательское средство удаления, если поведение по умолчанию delete не подходит для объекта. scoped_ptr и scoped_array не имеют этой способности, поскольку они довольно легкие по сравнению с shared_ptr и shared_array.

В C ++ 11, самой новой и текущей версии C ++, есть также unique_ptr, который аналогичен scoped_ptr, за исключением того, что вы можете передать право собственности на объект другому unique_ptr. В C ++ 03, более старой, но более широко поддерживаемой версии C ++, есть auto_ptr, что эквивалентно unique_ptr, за исключением того, что его было легко использовать небезопасным образом случайно (именно поэтому он устарел в C +) +11).

Должен ли я использовать scoped_ptr вместо shared_ptr всегда, когда объект не вызывает конструктор копирования?

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

Я также не понимаю идею общего / объемного массива. Разве я не могу просто использовать вместо него std :: vector?

std::vector не реализует семантику подсчета ссылок, как shared_array. std::vector больше похоже на scoped_array, но может быть скопировано. Когда вы копируете std::vector, вы также копируете все содержащиеся в нем элементы. Это не относится к scoped_array. std::vector также имеет функции, которые позволяют вам манипулировать и проверять его содержимое (например, push_back, insert, erase и т. Д.), Но гораздо тяжелее, чем scoped_array.

1 голос
/ 03 апреля 2012

Я бы сказал, что вы думаете об этом неправильно.Вопрос не в том, вызовете ли вы do конструктор копирования, а в том, нужно ли вам 1004 * вызвать конструктор копирования.Другими словами, вы должны решить, использовать ли shared_ptr или scoped_ptr не на основе чтения вашего кода, а на основе размышлений о владении объектом и о том, каким должно быть время жизни объекта.

Скажем, у вас есть объект, который вы хотитесоздать по куче, а не по стеку, по любой причине (может быть, он слишком большой, чтобы быть в стеке, может быть, вы захотите заменить его другим объектом в какой-то момент, может быть, вы хотите, чтобы он был инициализирован поздно), но еговремя жизни никогда не должно быть длиннее, чем его объем.Типичным примером этого являются переменные экземпляра в классе: их часто следует удалять при удалении объекта, в котором они находятся.Затем вы должны использовать scoped_ptr.

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

Так почему кто-то когда-либо использует scoped_ptr вместо shared_ptr?Прежде всего, знание того, когда вызывается деструктор, является одним из больших преимуществ неуправляемых языков, таких как C ++.Может быть, деструктор дорог, или, может быть, он освобождает ресурс;приятно знать, когда такие вещи случаются.Кроме того, с shared_ptr существует вероятность утечек памяти, если вы случайно создадите циклическую ссылку.

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

Что касается массивов scoped / shared, вы, безусловно, можете использовать std :: vector,но массивы дешевле, и иногда люди хотят получить выигрыш в производительности.

1 голос
/ 03 апреля 2012

Если вы выделите память, вы можете поместить вновь созданный указатель в указатель с областью действия, чтобы в случае сбоя malloc / noew память была освобождена. Это то, как я обычно использую его, или если это объект, который должен быть размещен в куче, но я хочу рассматривать его как объект стека с точки зрения того, что он будет живым только до конца области.

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

1 голос
/ 03 апреля 2012

shared_ptr очень отличается от scoped_ptr. scoped_ptr (который теперь стандартизирован в C ++ 11 как std::unique_ptr) - это просто интеллектуальный указатель в стиле RAII, который берет на себя владение ресурсом и затем освобождает принадлежащий ему ресурс, когда указатель выходит из области видимости.

A shared_ptr однако, может делиться правами владения ресурсом с другими экземплярами shared_ptr. Ресурс остается в живых, пока один или несколько экземпляров shared_ptr владеют им. Это метод автоматического управления памятью (форма сбора мусора), известный как подсчет ссылок . Он в основном обеспечивает тот же эффект, что и более продвинутые алгоритмы сбора мусора, за исключением того, что в отличие от других методов сбора мусора он не обрабатывает циклические ссылки.

Что касается использования std::vector против boost::scoped_array, да - scoped_array на самом деле не дает большого преимущества. Однако boost::shared_array предлагает семантику подсчета ссылок, как и shared_ptr.

1 голос
/ 03 апреля 2012

Да. scoped_ptr не позволяет копировать, в то время как shared_ptr делает. Но эта «простая» разница оказывает огромное влияние как на реализацию, так и на использование интеллектуального указателя.

scoped_ptr быстрее и легче, чем shared_ptr, потому что подсчет ссылок не используется. shared_ptr будет подсчитывать количество назначений и не удалит объект, пока все ссылки не истекли / не вышли из области видимости.

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

Вектор хранит список объектов. A XXX_ptr хранит указатель на (один) динамически размещаемый объект. Яблоки и апельсины.

...