Умный указатель - Smart pointer

В Информатика, а умный указатель является абстрактный тип данных который имитирует указатель предоставляя дополнительные функции, такие как автоматический управление памятью или же проверка границ. Такие функции предназначены для уменьшения количества ошибок, вызванных неправильным использованием указателей, при сохранении эффективности. Интеллектуальные указатели обычно отслеживают память, на которую они указывают, и также могут использоваться для управления другими ресурсами, такими как сетевые соединения и дескрипторы файлов. Умные указатели были впервые популяризированы в языке программирования C ++ в первой половине 1990-х годов как опровержение критики отсутствия в C ++ автоматический сбор мусора.[1][2]

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

Если язык поддерживает автоматическую сборку мусора (например, Ява или же C # ), то интеллектуальные указатели не нужны для восстановления и безопасности управления памятью, но полезны для других целей, таких как тайник структура данных управления резиденцией и Управление ресурсами таких объектов, как файловые ручки или же сетевые розетки.

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

История

Несмотря на то, что C ++ популяризировал концепцию интеллектуальных указателей, особенно подсчитанный по ссылкам Разнообразие, непосредственный предшественник одного из языков, вдохновивших разработку C ++, имел ссылки с подсчетом ссылок, встроенные в язык. C ++ частично был вдохновлен Simula67.[3] Предком Simula67 был Simula I. Поскольку Simula I. элемент аналогичен указателю C ++ без ноль, и поскольку процесс Simula I с фиктивным оператором в качестве тела его действия аналогичен C ++ структура (что само по себе аналогично работе C.A.R. Hoare's записывать в тогдашних работах 1960-х годов) в Simula I были элементы с подсчетом ссылок (то есть выражения-указатели, которые содержат косвенное обращение) к процессам (то есть записям) не позднее сентября 1965 года, как показано в приведенных ниже абзацах.[4]

На процессы можно ссылаться индивидуально. Физически ссылка на процесс - это указатель на область памяти, содержащую локальные для процесса данные и некоторую дополнительную информацию, определяющую его текущее состояние выполнения. Однако по причинам, указанным в разделе 2.2, ссылки на процессы всегда косвенные, через элементы, называемые элементы. Формально ссылка на процесс - это значение выражения типа элемент.



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

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

Стоит отметить сходство между процессом, тело активности которого является фиктивным оператором, и концепцией записи, недавно предложенной К. А. Р. Хоаром и Н. Виртом.

Поскольку C ++ заимствовал Симула подход к распределению памяти - новый ключевое слово при выделении процесса / записи для получения свежего элемент к этому процессу / записи - неудивительно, что C ++ в конечном итоге воскресил в Simula механизм интеллектуальных указателей с подсчетом ссылок в элемент также.

Функции

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

Умные указатели могут облегчить преднамеренное программирование выражая в типе способ управления памятью референта указателя. Например, если функция C ++ возвращает указатель, нет способа узнать, должен ли вызывающий объект удалить память референта, когда вызывающий закончит с информацией.

SomeType* Неоднозначный();  // Что делать с результатом?

Традиционно для разрешения неоднозначности использовались соглашения об именах,[5] который подвержен ошибкам и является трудоемким. C ++ 11 представил способ гарантировать правильное управление памятью в этом случае, объявив функцию, возвращающую unique_ptr,

стандартное::unique_ptr<SomeType> ObviousFunction();

Объявление типа возвращаемого значения функции как unique_ptr делает явным тот факт, что вызывающий объект становится владельцем результата, а среда выполнения C ++ гарантирует, что память будет освобождена автоматически. Перед C ++ 11, unique_ptr можно заменить на auto_ptr.

Создание новых объектов

Чтобы облегчить выделение

стандартное::shared_ptr<SomeType>

C ++ 11 введено:

авто s = стандартное::make_shared<SomeType>(конструктор, параметры, Вот);

и аналогично

стандартное::unique_ptr<some_type>

С C ++ 14 можно использовать:

авто ты = стандартное::make_unique<SomeType>(конструктор, параметры, Вот);

Практически во всех обстоятельствах предпочтительно использовать эти средства, а не новый ключевое слово:[6]

unique_ptr

C ++ 11 вводит std :: unique_ptr, определенный в заголовке <memory>.[7]

А unique_ptr это контейнер для необработанного указателя, который unique_ptr говорят, что владеет. А unique_ptr явно предотвращает копирование содержащегося в нем указателя (как это произошло бы при обычном назначении), но std :: move функция может использоваться для передачи владения содержащимся указателем другому unique_ptr. А unique_ptr не может быть скопирован, потому что его конструктор копирования и операторы присваивания явно удалены.

стандартное::unique_ptr<int> p1(новый int(5));стандартное::unique_ptr<int> p2 = p1;  // Ошибка компиляции.стандартное::unique_ptr<int> p3 = стандартное::двигаться(p1);  // Передает право собственности. p3 теперь владеет памятью, а p1 имеет значение nullptr.p3.перезагрузить();  // Удаляет память.p1.перезагрузить();  // Ничего не делает.

std ::auto_ptr является устарел под C ++ 11 и полностью удален из C ++ 17. Конструктор копирования и операторы присваивания auto_ptr фактически не копируйте сохраненный указатель. Вместо этого они передать это, оставив предыдущий auto_ptr объект пустой. Это был один из способов реализовать строгое владение, так что только один auto_ptr объект может владеть указателем в любой момент времени. Это означает, что auto_ptr не следует использовать там, где требуется семантика копирования.[8][нужна цитата ] С auto_ptr уже существовал с его семантикой копирования, его нельзя было обновить до указателя только для перемещения без нарушения Обратная совместимость с существующим кодом.

shared_ptr и weak_ptr

C ++ 11 представляет std :: shared_ptr и std :: weak_ptr, определенный в заголовке <memory>.[7] C ++ 11 также вводит std :: make_shared (std :: make_unique был введен в C ++ 14) для безопасного выделения динамической памяти в RAII парадигма.[9]

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

стандартное::shared_ptr<int> p0(новый int(5));  // Допустимо, выделяет 1 целое число и инициализирует его значением 5.стандартное::shared_ptr<int[]> p1(новый int[5]);  // Допустимо, выделяет 5 целых чисел.стандартное::shared_ptr<int[]> p2 = p1;  // Теперь память принадлежит обоим.p1.перезагрузить();  // Память все еще существует из-за p2.p2.перезагрузить();  // Освобождает память, так как никто другой не владеет памятью.

А weak_ptr это контейнер для необработанного указателя. Он создан как копия shared_ptr. Существование или уничтожение weak_ptr копии shared_ptr не влияют на shared_ptr или другие его копии. Ведь копии shared_ptr были уничтожены, все weak_ptr копии становятся пустыми.

стандартное::shared_ptr<int> p1 = стандартное::make_shared<int>(5);стандартное::weak_ptr<int> wp1 {p1};  // p1 владеет памятью.{  стандартное::shared_ptr<int> p2 = wp1.замок();  // Теперь p1 и p2 владеют памятью.  // p2 инициализируется слабым указателем, поэтому вам нужно проверить,  // память все еще существует!  если (p2) {    DoSomethingWith(p2);  }}// p2 уничтожен. Память принадлежит p1.p1.перезагрузить();  // Освободить память.стандартное::shared_ptr<int> p3 = wp1.замок(); // Памяти больше нет, поэтому мы получаем пустой shared_ptr.если (p3) {  // код не будет выполняться  ActionThatNeedsALivePointer(p3);}

Поскольку реализация shared_ptr использует подсчет ссылок, циркулярные ссылки потенциально проблема. Циркуляр shared_ptr цепочку можно разорвать, изменив код так, чтобы одна из ссылок была weak_ptr.

Несколько потоков могут безопасно одновременно обращаться к разным shared_ptr и weak_ptr объекты, указывающие на один и тот же объект.[10]

Указанный объект должен быть защищен отдельно, чтобы гарантировать безопасность потоков.

shared_ptr и weak_ptr основаны на версиях, используемых Библиотеки Boost.[нужна цитата ] Технический отчет C ++ 1 (TR1) впервые представил их в стандарте, так как общие коммунальные услуги, но C ++ 11 добавляет больше функций в соответствии с версией Boost.

Смотрите также

Рекомендации

  1. ^ Клайн, Маршалл (сентябрь 1997 г.). «Разделы часто задаваемых вопросов по C ++ Lite, посвященные интеллектуальным указателям с подсчетом ссылок и семантике копирования при записи в разделе часто задаваемых вопросов по управлению бесплатным хранилищем». cis.usouthal.edu. Получено 6 апреля 2018.
  2. ^ Колвин, Грегори (1994). "предложение по стандартизации countted_ptr в стандартной библиотеке C ++" (PDF). open-std.org. Получено 6 апреля 2018.
  3. ^ Страуструп, Бьярн. «История C ++: 1979–1991» (PDF). Получено 6 апреля 2018.
  4. ^ Даль, Оле-Йохан и Найгаард, Кристен (сентябрь 1966 г.). «SIMULA - язык моделирования на основе АЛГОЛА» (PDF). folk.uio.no. Получено 6 апреля 2018.CS1 maint: несколько имен: список авторов (связь)
  5. ^ «Руководство Taligent по разработке программ, раздел Использование специальных имен для копирования, создания и внедрения подпрограмм».
  6. ^ Саттер, Херб (20 апреля 2013 г.). «Отчет о поездке: встреча ISO C ++ Spring 2013». isocpp.org. Получено 14 июн 2013.
  7. ^ а б ISO 14882: 2011 20.7.1
  8. ^ CERT C ++ Стандарт безопасного кодирования
  9. ^ ISO 14882: 2014 20.7.1
  10. ^ boost :: shared_ptr потокобезопасность (формально не распространяется на std :: shared_ptr, но считается, что имеет те же ограничения потоковой передачи)

дальнейшее чтение

внешняя ссылка