Указатели, умные указатели или общие указатели?

Я программирую с помощью обычных указателей, но я слышал о таких библиотеках, как Boost, которые реализуют интеллектуальные указатели. Я также видел, что в движке рендеринга Ogre3D существует глубокое использование общих указателей.

В чем именно разница между тремя, и я должен придерживаться только их типа?

Ответы

Ответ 1

Сидий довольно хорошо изложил типы:

  • Нормальные указатели - это именно то, что они где-то указывают на что-то в памяти. Кому это принадлежит? Только комментарии дадут вам знать. Кто освобождает его? Надеюсь, владелец в какой-то момент.
  • Умные указатели - это общий термин, охватывающий многие типы; Предположим, вы имели в виду скопированный указатель, который использует шаблон RAII. Это объект, выделенный стеком, который обертывает указатель; когда он выходит из сферы действия, он вызывает delete на указателе, который он обертывает. Он "владеет" содержащимся указателем в том, что он отвечает за его удаление в какой-то момент. Они позволяют получить исходную ссылку на указатель, который они переносят для передачи другим методам, а также освобождение указателя, позволяя кому-то другому владеть им. Копирование их не имеет смысла.
  • Общие указатели - это выделенный стек элемент, который обертывает указатель, так что вам не нужно знать, кто его владеет. Когда уничтожается последний общий указатель для объекта в памяти, заверенный указатель также будет удален.

Как насчет того, когда вы должны их использовать? Вы либо сильно используете указатели с областью действия, либо общие указатели. Сколько потоков выполняется в вашем приложении? Если ответ "потенциально много", общие указатели могут оказаться узким местом производительности при использовании повсюду. Причина в том, что создание/копирование/разрушение общего указателя должно быть атомной операцией, и это может помешать работе, если у вас много потоков. Однако это не всегда так - только тестирование обязательно скажет вам.

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

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

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

Ответ 2

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

Тупые указатели (T *) никогда не являются лучшим решением. Они заставляют вас делать явное управление памятью, что является многословным, подверженным ошибкам, а иногда и невозможным. Но что более важно, они не сигнализируют о ваших намерениях.

Автоматические указатели удаляют указателя при уничтожении. Для массивов предпочитайте инкапсуляции, такие как vector и deque. Для других объектов очень редко нужно хранить их в куче - просто используйте локали и состав объектов. Тем не менее потребность в указателях автоматически возникает с функциями, возвращающими указатели кучи - например, фабрики и полиморфные возвращения.

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

Для важного аналога общих указателей также найдите слабые указатели.

Ответ 3

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

Ответ 4

Чтобы избежать утечек памяти, вы можете использовать интеллектуальные указатели, когда можете. В С++ есть два разных типа умных указателей.

  • Ссылка подсчитана (например, boost:: shared_ptr/std:: tr1: shared_ptr)
  • подсчет без ссылок (например, boost:: scoped_ptr/std:: auto_ptr)

Основное отличие состоит в том, что ссылки, подсчитанные на интеллектуальные указатели, могут быть скопированы (и использованы в std:: container), в то время как scoped_ptr не может. Не ссылающиеся указатели указателей почти не имеют накладных расходов или вообще не имеют накладных расходов. Счётчик ссылок всегда вводит какие-то накладные расходы.

(Я предлагаю избегать auto_ptr, он имеет некоторые серьезные недостатки при неправильном использовании)

Ответ 5

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