X Tutup
The Wayback Machine - https://web.archive.org/web/20220403214022/https://ru.cppreference.com/w/cpp/memory/shared_ptr
Пространства имён
Варианты
Действия

std::shared_ptr

Материал из cppreference.com
< cpp‎ | memory
 
 
Библиотека утилит
Поддержка языка
Поддержка типа (базовые типы, RTTI, свойства типов)    
Макросы тестирования функциональности библиотеки (C++20)
Управление динамической памятью
Программные утилиты
Обработка ошибок
Поддержка сопрограмм (C++20)
Вариативные функции
(C++17)
Трёхстороннее сравнение (C++20)
(C++20)
(C++20)(C++20)(C++20)(C++20)(C++20)(C++20)
Общие утилиты
Дата и время
Объекты функции
Библиотека форматирования (C++20)
(C++11)
Операторы отношения (устарело в C++20)
Целочисленные функции сравнения
(C++20)(C++20)(C++20)  
(C++20)
Операции обмена и типа
(C++14)
(C++11)

(C++11)
(C++11)
(C++17)
Общие лексические типы
(C++11)
(C++17)
(C++17)
(C++17)
(C++17)
(C++23)

Элементарные преобразования строк
(C++17)
(C++17)
 
Динамическое управление памятью
Умные указатели
(C++11)
shared_ptr
(C++11)
(C++11)
(до C++17)
(C++11)
(C++23)
Аллокаторы
Ресурсы памяти
Неинициализированное хранилище
Алгоритмы неинициализированной памяти
Ограниченные алгоритмы неинициализированной памяти
Поддержка сборки мусора
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)
(C++11)(до C++23)
Разное
(C++20)
(C++11)
(C++11)
 
 
Определено в заголовочном файле <memory>
template< class T > class shared_ptr;
(начиная с C++11)

std::shared_ptr это умный указатель, с разделяемым владением объектом через его указатель. Несколько указателей shared_ptr могут владеть одним и тем же объектом. Объект будет уничтожен и занятая им память будет освобождена в одном из следующих случаев:

  • когда последний shared_ptr, владеющий указателем на объект, будет уничтожен;
  • когда последнему shared_ptr, владеющему указателем на объект, будет назначен другой указатель с помощью operator= или reset().

Объект уничтожается с использованием выражения delete или с использованием пользовательской функции удаления объекта, переданной в конструктор shared_ptr.

std::shared_ptr может разделять владение объектом и в то же время хранить указатель на другой объект. Это позволяет владеть объектом и в то же время указывать на элемент этого объекта. Хранимый указатель это тот, к которому обращается get(), операторы разыменования и сравнения. Управляемый указатель это тот, который будет передан функции удаления объекта, когда счётчик владения достигнет нуля.

shared_ptr может не владеть ни одним объектом, в этом случае он называется пустым (пустой shared_ptr может иметь ненулевой сохранённый указатель, если был использован псевдонимный конструктор).

Все специализации shared_ptr отвечают требованиям CopyConstructible, CopyAssignable и LessThanComparable, а также неявно преобразуются в bool.

Все функции-элементы (включая конструктор копирования и присваивание копированием) могут быть вызваны в нескольких потоках разными экземплярами shared_ptr без дополнительной синхронизации, даже если эти экземпляры копируют и владеют одним и тем же объектом. Если несколько потоков обращаются к одному и тому же экземпляру shared_ptr без синхронизации и какое-либо обращение происходит через неконстантную функцию-элемент shared_ptr, тогда произойдёт гонка данных; для предотвращения гонки данных можно использовать перегрузку атомарных функций для shared_ptr.

Содержание

[править] Типы-элемент

Тип-элемент Определение
element_type
T (до C++17)
std::remove_extent_t<T> (начиная с C++17)
weak_type (начиная с C++17) std::weak_ptr<T>

[править] Функции-элементы

создаёт новый shared_ptr
(public функция-элемент) [править]
разрушает объект, которым владеет, если больше нет shared_ptr ссылающихся на него
(public функция-элемент) [править]
присваивает значение shared_ptr
(public функция-элемент) [править]
Модификаторы
заменяет управляемый объект
(public функция-элемент) [править]
обменивает управляемые объекты
(public функция-элемент) [править]
Наблюдатели
возвращает хранимый указатель
(public функция-элемент) [править]
разыменовывает сохранённый указатель
(public функция-элемент) [править]
обеспечивает индексированный доступ к сохранённому массиву
(public функция-элемент) [править]
возвращает количество объектов shared_ptr, ссылающихся на один и тот же управляемый объект
(public функция-элемент) [править]
(до C++20)
проверяет, управляется ли управляемый объект только текущим экземпляром shared_ptr
(public функция-элемент) [править]
проверяет, не является ли сохранённый указатель нулевым
(public функция-элемент) [править]
обеспечивает упорядочивание общих указателей на основе владельца
(public функция-элемент) [править]

[править] Функции, не являющиеся элементами

создаёт общий указатель, который управляет новым объектом
(шаблон функции) [править]
создаёт общий указатель, который управляет новым объектом, выделенным с помощью аллокатора
(шаблон функции) [править]
применяет static_cast, dynamic_cast, const_cast или reinterpret_cast к сохранённому указателю
(шаблон функции) [править]
возвращает пользовательскую функцию указанного типа, если владеет
(шаблон функции) [править]

(убрано в C++20)
(убрано в C++20)
(убрано в C++20)
(убрано в C++20)
(убрано в C++20)
(C++20)
сравнивает с другим shared_ptr или с nullptr
(шаблон функции) [править]
выводит значение сохранённого указателя в выходной поток
(шаблон функции) [править]
специализация алгоритма std::swap
(шаблон функции) [править]
специализации атомарных операций для std::shared_ptr
(шаблон функции) [править]

[править] Вспомогательные классы

атомарный разделяемый указатель
(специализация шаблона класса) [править]
поддержка хеширования для 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 может быть вызван с сырым указателем на неполный тип).

Тип T в std::shared_ptr<T> может быть функцией: в этом случае он владеет указателем на функцию вместо указателя на объект. Это иногда используется, чтобы держать динамическую библиотеку или плагин загруженными, пока указатель ссылается на принадлежащие им функции:

void del(void(*)()) {}
void fun() {}
int main(){
  std::shared_ptr<void()> ee(fun, del);
  (*ee)();
}

[править] Примечания по реализации

В типичной реализации, std::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]), если есть общий указатель на тот же блок управления.

Чтобы удовлетворить требования безопасности потоков, счётчики ссылок обычно инкрементируются с использованием эквивалента std::atomic::fetch_add с std::memory_order_relaxed (декремент требует более строгого упорядочивания для безопасного уничтожения блока управления).

[править] Пример

#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex>
 
struct Base
{
    Base() { std::cout << "  Base::Base()\n"; }
    // Примечание: невиртуальный деструктор здесь правилен
    ~Base() { std::cout << "  Base::~Base()\n"; }
};
 
struct Derived: public Base
{
    Derived() { std::cout << "  Derived::Derived()\n"; }
    ~Derived() { std::cout << "  Derived::~Derived()\n"; }
};
 
void thr(std::shared_ptr<Base> p)
{
    std::shared_ptr<Base> lp = p; // потокобезопасно, даже если
                                  // общий use_count инкрементируется
    {
        static std::mutex io_mutex;
        std::lock_guard<std::mutex> lk(io_mutex);
        std::cout << "локальный указатель в потоке:\n"
                  << "  lp.get() = " << lp.get()
                  << ", lp.use_count() = " << lp.use_count() << '\n';
    }
}
 
int main()
{
    std::shared_ptr<Base> p = std::make_shared<Derived>();
 
    std::cout << "Создан общий Derived (как указатель на Base)\n"
              << "  p.get() = " << p.get()
              << ", p.use_count() = " << p.use_count() << '\n';
    std::thread t1(thr, p), t2(thr, p), t3(thr, p);
    p.reset(); // освободить владение функцией main
    std::cout << "Создать совместное владение между 3-мя потоками и освободить\n"
              << "владение функцией main:\n"
              << "  p.get() = " << p.get()
              << ", p.use_count() = " << p.use_count() << '\n';
    t1.join(); t2.join(); t3.join();
    std::cout << "Все потоки завершены, последним удалён Derived\n";
}

Возможный вывод:

Base::Base()
  Derived::Derived()
Создан общий Derived (как указатель на Base)
  p.get() = 0x2299b30, p.use_count() = 1
Создать совместное владение между 3-мя потоками и освободить
владение функцией main:
  p.get() = 0, p.use_count() = 0
локальный указатель в потоке:
  lp.get() = 0x2299b30, lp.use_count() = 5
локальный указатель в потоке:
  lp.get() = 0x2299b30, lp.use_count() = 3
локальный указатель в потоке:
  lp.get() = 0x2299b30, lp.use_count() = 2
  Derived::~Derived()
  Base::~Base()
Все потоки завершены, последним удалён Derived

[править] Смотрите также

умный указатель с уникальной семантикой владения объектом
(шаблон класса) [править]
(C++11)
слабая ссылка на объект, управляемый std::shared_ptr
(шаблон класса) [править]
X Tutup