unique_ptr - значительное улучшение? - PullRequest
21 голосов
/ 05 июня 2009

В действующем стандарте C ++ создание коллекций, удовлетворяющих следующим правилам, трудно, если не невозможно:

  1. исключение безопасности,
  2. дешевые внутренние операции (в реальных контейнерах STL: операции являются копиями),
  3. автоматическое управление памятью.

Чтобы удовлетворить (1), коллекция не может хранить необработанные указатели. Для удовлетворения (2) коллекция должна хранить необработанные указатели. Для удовлетворения (3) коллекция должна хранить объекты по значению.

Заключение: три предмета конфликтуют друг с другом.

Элемент (2) не будет удовлетворен, когда используются shared_ptr s, потому что, когда коллекции потребуется переместить элемент, ей нужно будет сделать два вызова: в конструктор и деструктор. Никакие массивные memcpy() -подобные операции копирования / перемещения невозможны.

Я прав, что описанная проблема будет решена с помощью unique_ptr и std::move()? Коллекции, использующие инструменты, смогут удовлетворить все 3 условия:

  1. Когда коллекция будет удалена как побочный эффект исключения, она вызовет деструкторы unique_ptr. Нет утечки памяти.
    • unique_ptr не требуется дополнительное место для счетчика ссылок; поэтому его тело должно быть точно такого же размера, что и завернутый указатель,
    • Я не уверен, но похоже, что это позволяет перемещать группы unique_ptrs, используя memmove() подобные операции (? ),
    • , даже если это невозможно, оператор std::move() позволит перемещать каждый unique_ptr объект без вызова пары конструктор / деструктор.
  2. unique_ptr будет иметь исключительное право собственности на данную память. Случайные утечки памяти невозможны.

Это правда? Каковы другие преимущества использования unique_ptr?

Ответы [ 5 ]

6 голосов
/ 05 июня 2009

Я полностью согласен. Наконец-то появился естественный способ обработки объектов, выделенных из кучи.

В ответ на:

Я не уверен, но, похоже, это позволяет перемещать группы по unique_ptr с помощью memmove() подобных операций,

было предложение , позволяющее это сделать, но оно не вошло в стандарт C ++ 11.

2 голосов
/ 09 августа 2012

Когда коллекция будет удалена как побочный эффект исключения, она вызовет деструкторы unique_ptr. Нет утечки памяти.

Да, контейнер unique_ptr удовлетворит это.

unique_ptr не требует дополнительного места для счетчика ссылок; поэтому его тело должно быть точно такого же размера, что и завернутый указатель

Размер

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

Я не уверен, но похоже, что это позволяет перемещать группы unique_ptrs с помощью memmove (), как операции (?),

Абсолютно нет. unique_ptr не тривиальный класс; следовательно, это не может быть memmove d вокруг. Даже если бы это было так, вы не можете просто memmove их, потому что нужно вызывать деструкторы для оригиналов. Это должен быть memmove, за которым следует memset.

, даже если это невозможно, оператор std :: move () позволит переместить каждый объект unique_ptr без вызова пары конструктор / деструктор.

Также неверно. Движение не делает конструкторов и деструкторов не вызываемыми. unique_ptr, которые уничтожаются, должны быть уничтожены ; что требует вызова их деструкторов. Точно так же новым unique_ptr нужно называть их конструкторы; так начинается жизнь объекта.

Этого нельзя избежать; так работает C ++.

Однако это не то, о чем вам следует беспокоиться. Честно говоря, если вас беспокоит простой вызов конструктора / деструктора, вы либо в коде, который вы должны оптимизировать вручную (и, следовательно, писать свой собственный код), либо преждевременно оптимизируете свой код. Важно не то, называются ли конструкторы / деструкторы; важно то, насколько быстро получается полученный код.

unique_ptr будет иметь исключительное право собственности на данную память. Случайные утечки памяти невозможны.

Да, будет.

Лично я бы сказал, что вы делаете одно из следующих действий:

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

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

  • Не обращая внимания на альтернативы. А именно, Контейнеры указателей Boost . Кажется, у них есть все, что вы хотите. Они владеют указателями на свои объекты, но внешне они имеют семантику значений, а не семантику указателей. Они безопасны от исключений, и любое копирование происходит с указателями. Нет unique_ptr конструктор / деструктор "накладные расходы".

2 голосов
/ 05 июня 2009

Да, вы правы. Я бы только добавил, что это возможно благодаря ссылкам с r-значением.

1 голос
/ 06 июня 2009

Похоже, что три условия, которые я перечислил в своем посте, можно получить с помощью Boost Pointer Container Library .

0 голосов
/ 06 июня 2009

Этот вопрос иллюстрирует, почему я так люблю сборщик мусора Boehm (libgc). По причинам, связанным с управлением памятью, никогда не нужно ничего копировать, и действительно, владение памятью больше не должно упоминаться как часть API. Вы должны купить немного больше оперативной памяти, чтобы получить такую ​​же производительность процессора, но вы экономите сотни часов времени программистов. Вы решаете.

...