уникальный ПТР-серьезное улучшение?


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

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

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

Заключение: эти три пункта противоречат друг другу.

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

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

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

5 21

5 ответов:

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

В ответ на:

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

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

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

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

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

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

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

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

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

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

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

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

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

Unique_ptr будет иметь исключительное право собственности на данную память. Никаких случайных утечек памяти не будет.

Да, это будет.

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

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

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

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

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

Этот вопрос плохо объясняет, почему я так люблю Boehm garbage collector (libgc). Никогда нет необходимости копировать что-либо по причинам управления памятью, и действительно, владение памятью больше не должно упоминаться как часть API. Вы должны купить немного больше оперативной памяти, чтобы получить ту же производительность процессора, но вы экономите сотни часов времени программистов. Тебе решать.