std::shared_ptr
Определено в заголовочном файле <memory> | ||
|---|---|---|
template< class T > class shared_ptr; | (с C++11) |
std::shared_ptr — это умный указатель, который сохраняет совместное владение объектом через указатель. Несколько shared_ptr объектов могут владеть одним и тем же объектом. Объект уничтожается и его память освобождается, когда происходит одно из следующих событий:
- уничтожается последний оставшийся
shared_ptrобъект, владеющий объектом; - последнему оставшемуся
shared_ptrобъекту, владеющему объектом, присваивается другой указатель с помощьюoperator=илиreset().
Объект уничтожается с помощью выражения delete-expression или пользовательского обработчика удаления, который передаётся в shared_ptr во время создания.
shared_ptr может разделять владение объектом, храня указатель на другой объект. Эта функция может быть использована для указания на объекты-члены, одновременно владея объектом, к которому они относятся. Хранимый указатель является тем, к которому обращаются get(), операторы разыменования и сравнения. Управляемый указатель — это тот, который передаётся обработчику удаления, когда количество ссылок достигает нуля.
shared_ptr также может не владеть ни одним объектом, в этом случае он называется пустым (пустой shared_ptr может иметь не нулевой хранимый указатель, если для его создания использовался конструктор с алиасом).
Все специализации shared_ptr удовлетворяют требованиям CopyConstructible, CopyAssignable и LessThanComparable и являются контекстно преобразуемыми в bool.
Все члены функций (включая конструктор копирования и присваивание копированием) могут вызываться в нескольких потоках на разных экземплярах shared_ptr без дополнительной синхронизации, даже если эти экземпляры являются копиями и разделяют владение одним и тем же объектом. Если несколько потоков выполнения обращаются к одному экземпляру shared_ptr без синхронизации, и любое из этих обращений использует не-const член функции shared_ptr, то произойдёт гонка данных; можно использовать shared_ptr перегрузки атомарных функций для предотвращения гонки данных.
Типы членов
| Тип члена | Определение | ||||
|---|---|---|---|---|---|
element_type |
|
||||
weak_type (с C++17) | std::weak_ptr<T> |
Члены функций
создаёт новый shared_ptr (публичная функция-член) |
|
уничтожает управляемый объект, если больше shared_ptr объектов не ссылаются на него (публичная функция-член) |
|
присваивает shared_ptr (публичная функция-член) |
|
Модификаторы |
|
| заменяет управляемый объект (публичная функция-член) |
|
| меняет местами управляемые объекты (публичная функция-член) |
|
Наблюдатели |
|
| возвращает хранимый указатель (публичная функция-член) |
|
| разыменовывает хранимый указатель (публичная функция-член) |
|
|
(C++17) | предоставляет индексированный доступ к хранимому массиву (публичная функция-член) |
возвращает количество shared_ptr объектов, ссылающихся на тот же управляемый объект (публичная функция-член) |
|
|
(до C++20) | проверяет, является ли управляемый объект управляемым только текущим экземпляром shared_ptr (публичная функция-член) |
| проверяет, не равен ли хранимый указатель нулю (публичная функция-член) |
|
| обеспечивает упорядочение общих указателей на основе владельца (публичная функция-член) |
|
|
(C++26) | обеспечивает хеширование общих указателей на основе владельца (публичная функция-член) |
|
(C++26) | обеспечивает сравнение общих указателей на основе владельца (публичная функция-член) |
Внешние функции
|
(C++20) | создаёт указатель shared, управляющий новым объектом (шаблон функции) |
|
(C++20) | создаёт указатель shared, управляющий новым объектом, выделенным с помощью аллокатора (шаблон функции) |
|
(C++17) | применяет static_cast, dynamic_cast, const_cast или reinterpret_cast к хранимому указателю (шаблон функции) |
| возвращает удалитель указанного типа, если он принадлежит (шаблон функции) |
|
|
(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(удалено в C++20)(C++20) | сравнивает с другим shared_ptr или с nullptr (шаблон функции) |
| выводит значение хранимого указателя в поток вывода (шаблон функции) |
|
|
(C++11) | специализирует алгоритм std::swap (шаблон функции) |
|
(устарело в C++20) | специализирует атомарные операции для std::shared_ptr (шаблон функции) |
Вспомогательные классы
|
(C++20) | атомарный указатель shared (специализация шаблона класса) |
|
(C++11) | поддержка хеширования для std::shared_ptr (специализация шаблона класса) |
Указания по выводу типов (с C++17)
Примечания
Владение объектом может быть разделено только с другим shared_ptr путём копирующего построения или присваивания по значению другому shared_ptr. Создание нового shared_ptr с помощью необработанного базового указателя, принадлежащего другому shared_ptr, приведёт к неопределённому поведению.
std::shared_ptr может быть использован с неполным типом T. Однако конструктор из необработанного указателя (template<class Y> shared_ptr(Y*)) и член-функция template<class Y> void reset(Y*) могут быть вызваны только с указателем на полный тип (обратите внимание, что std::unique_ptr может быть создан из необработанного указателя на неполный тип).
Указатель в std::shared_ptr<T> может быть типом функции: в этом случае он управляет указателем на функцию, а не указателем на объект. Это иногда используется для сохранения загруженной динамической библиотеки или плагина до тех пор, пока к её функциям не будут обращаться:
void del(void(*)()) {}
void fun() {}
int main()
{
std::shared_ptr<void()> ee(fun, del);
(*ee)();
}Примечания по реализации
В типичной реализации shared_ptr содержит только два указателя:
- хранимый указатель (возвращаемый
get()); - указатель на блок управления.
Блок управления — это динамически выделенный объект, содержащий:
- либо указатель на управляемый объект, либо сам управляемый объект;
- удалитель (типизированный);
- аллокатор (типизированный);
- количество
shared_ptrвладельцев управляемого объекта; - количество
weak_ptrссылок на управляемый объект.
Когда shared_ptr создаётся вызовом std::make_shared или std::allocate_shared, память как для блока управления, так и для управляемого объекта создаётся с помощью одного выделения. Управляемый объект создаётся на месте в члене блока управления. Когда shared_ptr создаётся с помощью одного из конструкторов shared_ptr, управляемый объект и блок управления должны быть выделены отдельно. В этом случае блок управления хранит указатель на управляемый объект.
Указатель, хранимый в shared_ptr непосредственно, является тем, который возвращается get(), в то время как указатель/объект, хранимый в блоке управления, — это тот, который будет удалён, когда количество общих владельцев достигнет нуля. Эти указатели необязательно равны.
Деструктор shared_ptr уменьшает количество общих владельцев блока управления. Если этот счётчик достигнет нуля, блок управления вызывает деструктор управляемого объекта. Блок управления не освобождает себя до тех пор, пока счётчик std::weak_ptr ссылок не достигнет нуля.
В существующих реализациях количество слабых указателей увеличивается ([1], [2]), если есть ссылка shared указателя на тот же блок управления.
Для обеспечения требований к потоковой безопасности счётчики ссылок обычно увеличиваются с помощью эквивалента std::atomic::fetch_add с std::memory_order_relaxed (уменьшение требует более строгого порядка для безопасного уничтожения блока управления).
Пример
#include <chrono>
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
using namespace std::chrono_literals;
struct Base
{
Base() { std::cout << "Base::Base()\n"; }
// Note: non-virtual destructor is OK here
~Base() { std::cout << "Base::~Base()\n"; }
};
struct Derived : public Base
{
Derived() { std::cout << "Derived::Derived()\n"; }
~Derived() { std::cout << "Derived::~Derived()\n"; }
};
void print(auto rem, std::shared_ptr<Base> const& sp)
{
std::cout << rem << "\n\tget() = " << sp.get()
<< ", use_count() = " << sp.use_count() << '\n';
}
void thr(std::shared_ptr<Base> p)
{
std::this_thread::sleep_for(987ms);
std::shared_ptr<Base> lp = p; // thread-safe, even though the
// shared use_count is incremented
{
static std::mutex io_mutex;
std::lock_guard<std::mutex> lk(io_mutex);
print("Local pointer in a thread:", lp);
}
}
int main()
{
std::shared_ptr<Base> p = std::make_shared<Derived>();
print("Created a shared Derived (as a pointer to Base)", p);
std::thread t1{thr, p}, t2{thr, p}, t3{thr, p};
p.reset(); // release ownership from main
print("Shared ownership between 3 threads and released ownership from main:", p);
t1.join();
t2.join();
t3.join();
std::cout << "All threads completed, the last one deleted Derived.\n";
}Возможный вывод:
Base::Base()
Derived::Derived()
Created a shared Derived (as a pointer to Base)
get() = 0x118ac30, use_count() = 1
Shared ownership between 3 threads and released ownership from main:
get() = 0, use_count() = 0
Local pointer in a thread:
get() = 0x118ac30, use_count() = 5
Local pointer in a thread:
get() = 0x118ac30, use_count() = 4
Local pointer in a thread:
get() = 0x118ac30, use_count() = 2
Derived::~Derived()
Base::~Base()
All threads completed, the last one deleted Derived.Пример
#include <iostream>
#include <memory>
struct MyObj
{
MyObj() { std::cout << "MyObj constructed\n"; }
~MyObj() { std::cout << "MyObj destructed\n"; }
};
struct Container : std::enable_shared_from_this<Container> // note: public inheritance
{
std::shared_ptr<MyObj> memberObj;
void CreateMember() { memberObj = std::make_shared<MyObj>(); }
std::shared_ptr<MyObj> GetAsMyObj()
{
// Use an alias shared ptr for member
return std::shared_ptr<MyObj>(shared_from_this(), memberObj.get());
}
};
#define COUT(str) std::cout << '\n' << str << '\n'
#define DEMO(...) std::cout << #__VA_ARGS__ << " = " << __VA_ARGS__ << '\n'
int main()
{
COUT("Creating shared container");
std::shared_ptr<Container> cont = std::make_shared<Container>();
DEMO(cont.use_count());
DEMO(cont->memberObj.use_count());
COUT("Creating member");
cont->CreateMember();
DEMO(cont.use_count());
DEMO(cont->memberObj.use_count());
COUT("Creating another shared container");
std::shared_ptr<Container> cont2 = cont;
DEMO(cont.use_count());
DEMO(cont->memberObj.use_count());
DEMO(cont2.use_count());
DEMO(cont2->memberObj.use_count());
COUT("GetAsMyObj");
std::shared_ptr<MyObj> myobj1 = cont->GetAsMyObj();
DEMO(myobj1.use_count());
DEMO(cont.use_count());
DEMO(cont->memberObj.use_count());
DEMO(cont2.use_count());
DEMO(cont2->memberObj.use_count());
COUT("Copying alias obj");
std::shared_ptr<MyObj> myobj2 = myobj1;
DEMO(myobj1.use_count());
DEMO(myobj2.use_count());
DEMO(cont.use_count());
DEMO(cont->memberObj.use_count());
DEMO(cont2.use_count());
DEMO(cont2->memberObj.use_count());
COUT("Resetting cont2");
cont2.reset();
DEMO(myobj1.use_count());
DEMO(myobj2.use_count());
DEMO(cont.use_count());
DEMO(cont->memberObj.use_count());
COUT("Resetting myobj2");
myobj2.reset();
DEMO(myobj1.use_count());
DEMO(cont.use_count());
DEMO(cont->memberObj.use_count());
COUT("Resetting cont");
cont.reset();
DEMO(myobj1.use_count());
DEMO(cont.use_count());
}Вывод:
Creating shared container cont.use_count() = 1 cont->memberObj.use_count() = 0 Creating member MyObj constructed cont.use_count() = 1 cont->memberObj.use_count() = 1 Creating another shared container cont.use_count() = 2 cont->memberObj.use_count() = 1 cont2.use_count() = 2 cont2->memberObj.use_count() = 1 GetAsMyObj myobj1.use_count() = 3 cont.use_count() = 3 cont->memberObj.use_count() = 1 cont2.use_count() = 3 cont2->memberObj.use_count() = 1 Copying alias obj myobj1.use_count() = 4 myobj2.use_count() = 4 cont.use_count() = 4 cont->memberObj.use_count() = 1 cont2.use_count() = 4 cont2->memberObj.use_count() = 1 Resetting cont2 myobj1.use_count() = 3 myobj2.use_count() = 3 cont.use_count() = 3 cont->memberObj.use_count() = 1 Resetting myobj2 myobj1.use_count() = 2 cont.use_count() = 2 cont->memberObj.use_count() = 1 Resetting cont myobj1.use_count() = 1 cont.use_count() = 0 MyObj destructed
См. также
|
(C++11) | интеллектуальный указатель с уникальной семантикой владения объектом (шаблон класса) |
|
(C++11) | слабая ссылка на объект, управляемый std::shared_ptr (шаблон класса) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/memory/shared_ptr