Умный указатель
Эта статья нуждается в дополнительных цитатах для проверки . ( июнь 2015 г. ) |
В информатике интеллектуальный указатель — это абстрактный тип данных , который имитирует указатель , предоставляя дополнительные функции, такие как автоматическое управление памятью или проверка границ . Такие функции предназначены для уменьшения количества ошибок, вызванных неправильным использованием указателей, сохраняя при этом эффективность. Интеллектуальные указатели обычно отслеживают память, на которую они указывают, а также могут использоваться для управления другими ресурсами, такими как сетевые подключения и дескрипторы файлов . Интеллектуальные указатели были впервые популяризированы в языке программирования C++ в первой половине 1990-х годов как опровержение критики в отношении отсутствия в C++ автоматической сборки мусора . [ 1 ] [ 2 ]
Неправильное использование указателей может стать основным источником ошибок. Интеллектуальные указатели предотвращают большинство ситуаций утечек памяти , делая освобождение памяти автоматическим. В более общем смысле, они делают уничтожение объекта автоматическим: объект, управляемый интеллектуальным указателем, автоматически уничтожается ( финализируется и затем освобождается), когда уничтожается последний (или единственный) владелец объекта, например, потому, что владельцем является локальная переменная , и действия переменной выполнение выходит за пределы области . Интеллектуальные указатели также устраняют висячие указатели , откладывая уничтожение до тех пор, пока объект больше не будет использоваться.
Если язык поддерживает автоматическую сборку мусора (например, Java или C# ), тогда интеллектуальные указатели не нужны для освобождения и безопасности управления памятью, но полезны для других целей, таких как управление местонахождением структуры данных кэша и управление ресурсами таких объектов, как как дескрипторы файлов или сетевые сокеты .
Существует несколько типов интеллектуальных указателей. Некоторые работают с подсчетом ссылок , другие — присваивая право собственности на объект одному указателю.
История
[ редактировать ]Несмотря на то, что C++ популяризировал концепцию интеллектуальных указателей, особенно их разновидность с подсчетом ссылок , [ 3 ] непосредственный предшественник одного из языков, вдохновивших разработку C++, имел встроенные в язык ссылки с подсчетом ссылок. C++ частично был вдохновлен Simula67 . [ 4 ] Предком Simula67 была Simula I. Поскольку элемент Simula I аналогичен указателю C++ без null и поскольку процесс Simula I с фиктивным оператором в качестве тела активности аналогичен структуре C++ ( которая сама по себе аналогична CAR Hoare в записи современной на тот момент работе 1960-х годов ), ), Simula У меня были элементы с подсчетом ссылок (т. е. выражения-указатели, содержащие косвенность) на процессы (т. е. записи) нет позже сентября 1965 года, как показано в цитируемых ниже параграфах. [ 5 ]
На процессы можно ссылаться индивидуально. Физически ссылка на процесс — это указатель на область памяти, содержащую локальные для процесса данные и некоторую дополнительную информацию, определяющую его текущее состояние выполнения. Однако по причинам, указанным в разделе 2.2, ссылки на процессы всегда являются косвенными, через элементы, называемые элементами. Формально ссылкой на процесс является значение выражения типа element .
…
Значения элементов можно сохранять и извлекать путем присвоения и ссылок на переменные элемента , а также другими способами.
Язык содержит механизм, позволяющий сделать атрибуты процесса доступными извне, т. е. изнутри других процессов. Это называется удаленным доступом. Таким образом, процесс представляет собой структуру данных, на которую можно ссылаться.
Стоит отметить сходство между процессом, телом активности которого является фиктивный оператор, и концепцией записи, недавно предложенной К. А. Хоаром и Н. Виртом.
Поскольку C++ заимствовал Simula подход к выделению памяти — новое ключевое слово при выделении процесса/записи для получения нового элемента для этого процесса/записи — неудивительно, что C++ в конечном итоге возродил механизм интеллектуальных указателей Simula с подсчетом ссылок внутри элемента как хорошо.
Функции
[ редактировать ]В C++ интеллектуальный указатель реализован как класс шаблона, который имитирует посредством перегрузки операторов поведение традиционного (необработанного) указателя (например, разыменование, присваивание), обеспечивая при этом дополнительные функции управления памятью.
Интеллектуальные указатели могут облегчить намеренное программирование , выражая в типе, как будет управляться память референта указателя. Например, если функция C++ возвращает указатель, невозможно узнать, должен ли вызывающий объект удалить память референта, когда вызывающий объект закончил обработку информации.
SomeType* AmbiguousFunction(); // What should be done with the result?
Традиционно для устранения двусмысленности использовались соглашения об именах. [ 6 ] это подверженный ошибкам и трудоемкий подход. В C++11 в этом случае появился способ обеспечить правильное управление памятью путем объявления функции, возвращающей unique_ptr
,
std::unique_ptr<SomeType> ObviousFunction();
Объявление типа возвращаемого значения функции как unique_ptr
явно указывает на то, что вызывающая сторона становится владельцем результата, а среда выполнения C++ гарантирует, что память будет освобождена автоматически. До C++11 unique_ptr можно было заменить на auto_ptr , который сейчас устарел.
Создание новых объектов
[ редактировать ]Чтобы облегчить распределение
std::shared_ptr<SomeType>
С++ 11 представил:
auto s = std::make_shared<SomeType>(constructor, parameters, here);
и аналогично
std::unique_ptr<some_type>
Начиная с С++ 14, можно использовать:
auto u = std::make_unique<SomeType>(constructor, parameters, here);
Почти во всех обстоятельствах предпочтительнее использовать эти средства, а не new
ключевое слово. [ 7 ]
уникальный_ptr
[ редактировать ]C++11 представляет std::unique_ptr
, определенный в заголовке <memory>
. [ 8 ]
А unique_ptr
представляет собой контейнер для необработанного указателя, который unique_ptr
Говорят, что он владеет. А unique_ptr
явно предотвращает копирование содержащегося в нем указателя (как это происходит при обычном присваивании), но std::move
функция может использоваться для передачи владения содержащимся указателем другому unique_ptr
. А unique_ptr
не может быть скопирован, поскольку его конструктор копирования и операторы присваивания явно удалены.
std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = p1; // Compile error.
std::unique_ptr<int> p3 = std::move(p1); // Transfers ownership. p3 now owns the memory and p1 is set to nullptr.
p3.reset(); // Deletes the memory.
p1.reset(); // Does nothing.
std::auto_ptr
устарел в C++11 и полностью удален из C++17 . Конструктор копирования и операторы присваивания auto_ptr
на самом деле не копируйте сохраненный указатель. Вместо этого они передают его , оставляя предыдущий auto_ptr
объект пустой. Это был один из способов реализации строгой собственности, чтобы только один auto_ptr
объект может владеть указателем в любой момент времени. Это означает, что auto_ptr
не следует использовать там, где необходима семантика копирования. [ 9 ] [ нужна ссылка ] С auto_ptr
уже существовал со своей семантикой копирования, его нельзя было обновить до указателя, предназначенного только для перемещения, без нарушения обратной совместимости с существующим кодом.
общий_ptr и слабый_ptr
[ редактировать ]C++11 представляет std::shared_ptr
и std::weak_ptr
, определенный в заголовке <memory>
. [ 8 ] C++11 также представляет std::make_shared
( std::make_unique
был представлен в C++14) для безопасного выделения динамической памяти в парадигме RAII . [ 10 ]
А shared_ptr
является контейнером для необработанного указателя . Он поддерживает подсчет ссылок на содержащийся в нем указатель совместно со всеми копиями объекта. shared_ptr
. Объект, на который ссылается содержащийся необработанный указатель, будет уничтожен тогда и только тогда, когда все копии shared_ptr
были уничтожены.
std::shared_ptr<int> p0(new int(5)); // Valid, allocates 1 integer and initialize it with value 5.
std::shared_ptr<int[]> p1(new int[5]); // Valid, allocates 5 integers.
std::shared_ptr<int[]> p2 = p1; // Both now own the memory.
p1.reset(); // Memory still exists, due to p2.
p2.reset(); // Frees the memory, since no one else owns the memory.
А weak_ptr
является контейнером для необработанного указателя. Он создается как копия shared_ptr
. Существование или уничтожение weak_ptr
копии shared_ptr
не оказывают никакого влияния на shared_ptr
или другие его копии. Ведь копии shared_ptr
были уничтожены, все weak_ptr
копии становятся пустыми.
std::shared_ptr<int> p1 = std::make_shared<int>(5);
std::weak_ptr<int> wp1 {p1}; // p1 owns the memory.
{
std::shared_ptr<int> p2 = wp1.lock(); // Now p1 and p2 own the memory.
// p2 is initialized from a weak pointer, so you have to check if the
// memory still exists!
if (p2) {
DoSomethingWith(p2);
}
}
// p2 is destroyed. Memory is owned by p1.
p1.reset(); // Free the memory.
std::shared_ptr<int> p3 = wp1.lock();
// Memory is gone, so we get an empty shared_ptr.
if (p3) { // code will not execute
ActionThatNeedsALivePointer(p3);
}
Поскольку реализация shared_ptr
использует подсчет ссылок , циклические ссылки могут стать проблемой. Циркуляр shared_ptr
цепочку можно разорвать, изменив код так, чтобы одна из ссылок была weak_ptr
.
Несколько потоков могут безопасно одновременно обращаться к различным shared_ptr
и weak_ptr
объекты, указывающие на один и тот же объект. [ 11 ]
Объект, на который ссылаются, должен быть защищен отдельно, чтобы обеспечить потокобезопасность .
shared_ptr
и weak_ptr
основаны на версиях, используемых библиотеками Boost . [ нужна ссылка ] Технический отчет C++ 1 (TR1) впервые представил их в стандарте как общие утилиты , но в C++11 добавлено больше функций, как и в версии Boost.
Другие типы интеллектуальных указателей
[ редактировать ]Существуют и другие типы интеллектуальных указателей (которые не входят в стандарт C++), реализованные в популярных библиотеках C++ или пользовательских STL . Некоторые примеры включают указатель опасности. [ 12 ] и навязчивый указатель. [ 13 ] [ 14 ]
См. также
[ редактировать ]- auto_ptr
- Жирный указатель
- Теговый указатель
- Непрозрачный указатель
- Справочник (информатика)
- Boost (библиотеки C++)
- Автоматический подсчет ссылок
- Получение ресурсов — это инициализация (RAII)
- Сбор мусора в компьютерном программировании
Ссылки
[ редактировать ]- ^ Клайн, Маршалл (сентябрь 1997 г.). «Часто задаваемые вопросы по C++ Lite, посвященные интеллектуальным указателям с подсчетом ссылок и семантике ссылок при копировании при записи в часто задаваемых вопросах по управлению бесплатным хранилищем» . cis.usouthal.edu . Проверено 6 апреля 2018 г.
- ^ Колвин, Грегори (1994). «Предложение по стандартизации counted_ptr в стандартной библиотеке C++» (PDF) . open-std.org . Проверено 6 апреля 2018 г.
- ^ Клабник, Стив; Николс, Кэрол (2023) [2018]. «15. Умные указатели». Язык программирования Rust (2-е изд.). Сан-Франциско, Калифорния, США: No Starch Press, Inc., стр. 315–351. ISBN 978-1-7185-0310-6 . (xxix+1+527+3 страницы)
- ^ Страуструп, Бьерн. «История C++: 1979–1991» (PDF) . Проверено 6 апреля 2018 г.
- ^ Даль, Оле-Йохан; Найгаард, Кристен (сентябрь 1966 г.). «SIMULA — язык моделирования на основе ALGOL» (PDF) . фолк.уйо.но. Проверено 6 апреля 2018 г.
- ^ «Руководство Taligent по разработке программ», раздел «Используйте специальные имена для копирования, создания и внедрения процедур» .
- ^ Саттер, Херб (20 апреля 2013 г.). «Отчет о поездке: встреча ISO C++ весной 2013 г.» . isocpp.org . Проверено 14 июня 2013 г.
- ^ Jump up to: а б ИСО 14882:2011 20.7.1
- ^ CERT Стандарт безопасного кодирования C++
- ^ ИСО 14882:2014 20.7.1
- ^ «boost::shared_ptr потокобезопасность» . (Примечание. Формально не распространяется на std::shared_ptr, но считается, что он имеет те же ограничения на потоки.)
- ^ «folly/Hazptr.h на главной · facebook/folly» . github.com .
- ^ «Boost.SmartPtr: библиотека интеллектуальных указателей — 1.81.0» . boost.org .
- ^ «EASTL/intrusive_ptr.h в мастере · Electronicarts/EASTL» . github.com .
Дальнейшее чтение
[ редактировать ]- Мейерс, Скотт (2014). Эффективный современный C++ . Севастополь, Калифорния, США: O'Reilly Media . ISBN 978-1-49190399-5 . OCLC 884480640 .
- Александреску, Андрей (2001). «Умные указатели» . Современный дизайн на C++: применение общих шаблонов программирования и проектирования . Аддисон-Уэсли .
- Саттер, Херб (1 августа 2002 г.). «Новый C++: умные указатели» .
- Умные указатели – что, почему, что? . Йонат Шарон
- Обзор интеллектуальных указателей . Джон М. Длугош