Десять видов смарт-поинтеров вы будете ржать

Десять видов смарт-поинтеров вы будете ржать

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 или его наследника, живущего на стеке.

Report Page