Умный указательУмный указатель (англ. smart pointer) — идиома косвенного обращения к памяти, которая широко используется при программировании на высокоуровневых языках как: C++, Rust и так далее. Как правило, реализуется в виде специализированного класса (обычно — параметризованного), имитирующего интерфейс обычного указателя и добавляющего необходимую новую функциональность (например — проверку границ при доступе или очистку памяти)[1]. Как правило, основной целью задействования умных указателей является инкапсуляция работы с динамической памятью таким образом, чтобы свойства и поведение умных указателей имитировали свойства и поведение обычных указателей. При этом на них возлагается обязанность своевременного и аккуратного высвобождения выделенных ресурсов, что упрощает разработку кода и процесс отладки, исключая утечки памяти и возникновение висячих ссылок[2]. Указатели совместного владения (с подсчётом ссылок)Такие обычно используются с объектами, имеющими специальные операции «увеличить число ссылок» ( При появлении новой ссылки на объект вызывается операция «увеличить число ссылок», а при уничтожении — «уменьшить число ссылок». Если в результате операции «уменьшить число ссылок» число ссылок на объект становится равным нулю, то объект удаляется. Такая методика называется автоматическим подсчётом ссылок. Она согласует число указателей, хранящих адрес объекта, с числом ссылок, хранящимся в объекте, а при достижении этим числом нулевого значения приводит к удалению объекта. Её преимуществами являются относительно высокие надёжность, быстродействие и простота реализации в C++. Недостатком является усложнение использования в случае возникновения циклических ссылок (необходимость пользоваться «слабыми ссылками»). РеализацииСуществуют два вида таких указателей: с хранением счётчика внутри объекта и с хранением счётчика снаружи. Самый простой из вариантов — хранение счётчика внутри управляемого объекта. В COM объекты с подсчётом ссылок реализуются следующим образом:
Сходным образом реализован В
Поскольку структура-счётчик невелика, она может выделяться, например, через объектный пул. Проблема циклических ссылокПредположим, есть два объекта и в каждом из них по владеющему указателю. Указателю в первом объекте присвоим адрес второго объекта, а указателю во втором — адрес первого объекта. Если теперь всем внешним (то есть не хранящимся внутри этих объектов) указателям на два данных объекта присвоить новые значения, то указатели внутри объектов по-прежнему будут владеть друг другом и будут оставаться в памяти. В результате возникнет ситуация, когда к объектам невозможно получить доступ, то есть утечка памяти. Проблема циклических ссылок решается либо путём соответствующего проектирования структур данных, либо использованием сборки мусора, либо использованием двух видов ссылок: сильные (владеющие) и слабые (невладеющие, напр. Примеры реализаций
Указатели единоличного владенияЧасто указатели совместного владения слишком большие и «тяжёлые» для задач программиста: например, нужно создать объект одного из N типов, владеть им, время от времени обращаясь к его виртуальным функциям, а потом корректно удалить. Для этого используется «младший брат» — указатель единоличного владения. Такие указатели при присвоении нового значения или удалении сами удаляют объект. Присвоение указателей единоличного владения возможно только с разрушением одного из указателей — таким образом, никогда не будет ситуации, что два указателя владеют одним объектом. Их недостатком являются трудности с передачей объекта за пределы области видимости указателя. Примеры реализаций
Указатели на чужой буфер памятиВ большинстве случаев, если есть функция, имеющая дело с массивом, пишут одно из двух: void sort(size_t size, int* data); // указатель + размер
void sort(std::vector<int>& data); // конкретная структура памяти
Первое исключает автоматическую проверку диапазона. Второе ограничивает применимость Потому в развитых библиотеках для функций, которые пользуются чужими буферами памяти, используют «лёгкие» типы данных наподобие template <class T>
struct Buf1d {
T* data;
size_t size;
Buf1d(std::vector<T>& vec);
T& operator [](size_t i);
};
Нередко используется для строк: синтаксическим разборам, обеспечению жизнедеятельности текстового редактора и прочим специфичным задачам нужны собственные структуры данных, более быстрые, чем стандартные методы работы со строками. Примеры реализаций
Примечания
Ссылки
|
Portal di Ensiklopedia Dunia