Десять видов смарт-поинтеров вы будете ржать
Sehnsucht (https://t.me/cxx95)
Смарт-поинтер - это какая-то надстройка над обычным указателем T*, возможно с какими-то дополнительными полями (делетер, счетчик ссылок, etc.)
Здесь описаны различные смарт-поинтеры, которые мне известны.
std::unique_ptr<T> [C++11]
Смарт-поинтер, широко известный в нашей версии вселенной. Некопируемый, единоличное владение, можно указать свой делетер.
std::shared_ptr<T> [C++11]
И его младший партнер std::weak_ptr<T>. Множественное владение, есть внешний счетчик ссылок на куче, можно построить его рядом с объектом через std::make_shared.
boost::smart_ptr<T> [Boost]
Дока. Похож на std::unique_ptr<T>, но больше ограничений - нельзя нормально мувнуть в другой объект (только через вызов swap()) или release-нуть содержимый объект.
std::indirect<T> [C++26]
Гайд по нему. Немного похож на std::unique_ptr<T>, но поддержаны оператор и конструктор копирования из другого indirect, а также храниться может только объект T. Также в отличие от std::unique_ptr<T> объект всегда создается, и единственный кейс когда его может не быть это когда объект мувнули, есть флаг bool valueless_after_move() для проверки этого состояния.
std::polymorphic<T> [C++26]
Пропозал. Выглядит практически один в один как std::indirect<T> но хранить можно любой тип который наследуется от T, и при копировании с помощью type erasure обеспечивается копия именно унаследованного типа.
gsl::not_null<T> [Guidelines Support Library]
Реализация. Если часто имеем дело с указателями, которых надо проверять на nullptr перед разыменованием, C++ Core Guidelines рекомендует использовать тип gsl::not_null<T*> - это обертка над T*, где проверка на != nullptr сделана уже в конструкторе, поэтому пользователям объекта уже не требуется делать таких проверок самому.
xxx::observer_ptr<T> [пропозал и частные реализации]
Самый тупой указатель, не делает вообще ничего, кроме как защищает обернутый T* ptr от того что его могут удалить через delete ptr. Есть отказ от ввода в стандарт и дока на cppreference.
boost::intrusive_ptr<T> [Boost]
Дока. Похож на std::shared_ptr<T>, но счетчик ссылок находится прямо в самом объекте, что в 100% случаев обеспечивает локальность данных, которая в первом случае обеспечивается только через std::make_shared. Можно самому реализовать счетчик ссылок или унаследовать класс T от готового boost::intrusive_ref_counter<T>.
xxx::tracking_ptr<T> [частные реализации]
Цикл статей. Интересный концепт указателя, который "следит" за объектом даже если его мувнут.
struct Widget : trackable_object<Widget> { /* ... */ };
Widget w;
tracking_ptr<Widget> p(w);
assert(p.get() == &w);
p->Toggle(); // same as w.Toggle()
Widget moved = std::move(w);
assert(p.get() == &moved);
p->Toggle(); // same as moved.Toggle()
std::hazard_pointer [C++26]
Wiki, cppreference. Не совсем умный указатель в общем понимании, но решил оставить. Используется в lock-free алгоритмах. Полное описание выходит за пределы этой заметки.
xxx::versioned_ptr<T> [частные реализации]
Также не совсем умный указатель в общем понимании. Версионированные указатели иногда используются в lock-free алгоритмах (для фикса приколов как "ABA problem") вместо hazard pointer'ов. Это обертка над T* с "версией" рядом. Имеет ограниченное применение - тип версии должен иметь размер sizeof(T*) (например uintptr_t подойдет), и проц должен поддерживать compare-and-swap шириной 2*sizeof(T*). Полное описание выходит за пределы этой заметки.
Умные указатели для garbage collector [Oilpan]
Дока, презентация. Есть такой браузерный движок Blink (часть Chromium). Это такой монолит, где зависимости между разными объектами настолько сложные, что понимание общей картины - нереально для человека. И там есть МНОГО циклических зависимостей, потому что в какой-то момент архитектура бронзовеет и ее не переделать. Чтобы циклов не было, писали примерно так:
class A {
RefPtr<B> m_b;
};
class B {
A* m_a;
};
В какой-то момент всё было настолько плохо, что память протекала в 10% тестов. Решили проблему, добавив сборщик мусора. Это не особо повлияло на перф, но убрало многие протекания и краши - win!
Чтобы сборщик мусора нормально работал, все указатели за редкими исключениями должны быть обернуты в умные указатели Persistent<T>, Member<T> или WeakMember<T>.
xxx::static_ptr<T> [частные реализации]
Habr. Не мог отказать себе в ссылке на мою статью трехлетней давности, где я предлагаю реализацию придуманного мной static_ptr<T> который являет собой реализацию умного указателя похожего на std::unique_ptr<T> для класса T или его наследника, живущего на стеке.