Этот сайт использует cookies. Продолжение работы с сайтом означает, что Вы согласны!
Класс shared_ptr: совместно используемый указатель
Шаблонный класс shared_ptr
реализует совместно используемый указатель. Такой указатель можно и копировать, и перемещать. Количество копий отслеживается с помощью счетчика. Когда счетчик становится равным нулю, выполняется освобождение динамической памяти. Объявление класса shared_ptr
:
template<typename _Tp>
class shared_ptr : public __shared_ptr<_Tp>;
Управление одним объектом
Создать экземпляр класса shared_ptr
можно следующими основными способами (полный список смотрите в документации):
// #include <memory>
std::shared_ptr<B> ptr1;
std::shared_ptr<B> ptr2(nullptr);
std::shared_ptr<B> ptr3(new B(20));
std::shared_ptr<B> ptr4(ptr3);
std::unique_ptr<B> uptr(new B(5));
std::shared_ptr<B> ptr5(std::move(uptr));
std::cout << ptr1.use_count() << std::endl; // 0
std::cout << ptr2.use_count() << std::endl; // 0
std::cout << ptr3.use_count() << std::endl; // 2
std::cout << ptr4.use_count() << std::endl; // 2
std::cout << ptr5.use_count() << std::endl; // 1
std::cout << (uptr == nullptr) << std::endl; // 1
После названия класса shared_ptr
внутри угловых скобок задается название класса объекта или другой тип данных, например, int
. Первые две инструкции создают объекты с нулевым указателем, а третья инструкция при создании сразу принимает право владения экземпляром класса B
, созданном в динамической памяти. Четвертая инструкция создает копию «умного» указателя. В результате счетчик ссылок у обоих указателей будет равен 2
. Получить значение счетчика позволяет метод use_count()
. В пятой инструкции создается указатель класса unique_ptr
, а в шестой инструкции выполняется передача владения объектом указателю класса shared_ptr
. При этом объект uptr
будет хранить нулевой указатель.
Когда счетчик становится равным нулю, выполняется освобождение динамической памяти, причем только один раз:
{
std::shared_ptr<B> ptr1(new B(20)); // B::B()
std::cout << ptr1.use_count() << std::endl; // 1
{
std::shared_ptr<B> ptr2(ptr1);
std::cout << ptr1.use_count() << std::endl; // 2
std::cout << ptr2.use_count() << std::endl; // 2
}
std::cout << ptr1.use_count() << std::endl; // 1
} // B::~B()
// Динамическая память освобождается
При динамическом создании объекта может возникнуть исключение еще до получения права владения. Чтобы этого избежать следует воспользоваться функцией make_shared()
. Прототип функции:
template<typename _Tp, typename... Args>
inline shared_ptr<_Tp>
make_shared(Args &&... __args)
Пример:
std::shared_ptr<A> ptrA = std::make_shared<A>();
std::cout << ptrA->x << std::endl; // 0
auto ptrB = std::make_shared<B>(20);
std::cout << ptrB->y << std::endl; // 20
Класс shared_ptr
перегружает операторы ->
(доступ к члену) и *
(разыменование), а также операторы сравнения:
auto ptrB = std::make_shared<B>(20); // B::B()
std::cout << ptrB->y << std::endl; // 20
std::cout << (*ptrB).y << std::endl; // 20
auto ptr = std::make_shared<int>(30);
std::cout << *ptr << std::endl; // 30
ptrB.reset(); // Очистка (B::~B())
if (!ptrB) std::cout << "null" << std::endl; // null
Кроме того, выполнена перегрузка оператора <<
, позволяющего вывести результат выполнения метода get()
на консоль:
auto ptr = std::make_shared<int>(10);
std::cout << ptr << std::endl; // Адрес (например, 0x586e60)
std::cout << *ptr << std::endl; // Значение (10)
Класс shared_ptr
реализует совместно используемый указатель. Такой указатель можно и копировать, и перемещать:
std::shared_ptr<B> ptr1;
std::shared_ptr<B> ptr2;
std::shared_ptr<B> ptr3 = std::make_shared<B>(20);
// Можно создать копию
ptr1 = ptr3;
std::cout << ptr1.use_count() << std::endl; // 2
// Можно перемещать
ptr2 = std::move(ptr1);
std::cout << ptr2.use_count() << std::endl; // 2
std::cout << ptr1.use_count() << std::endl; // 0
// Можно перемещать из unique_ptr
std::unique_ptr<B> uptr(new B(5));
ptr1 = std::move(uptr);
std::cout << ptr1.use_count() << std::endl; // 1
Перечислим основные методы класса shared_ptr
:
use_count()
— возвращает количество объектов, совместно использующих указатель (возвращает значение счетчика):
auto ptr = std::make_shared<B>(20);
std::cout << ptr.use_count() << std::endl; // 1
unique()
— возвращает значениеtrue
, если значение счетчика равно1
, иfalse
— в противном случае:
auto ptr = std::make_shared<B>(20);
if (ptr.unique())
std::cout << "unique" << std::endl; // unique
else std::cout << "no" << std::endl;
get()
— возвращает обычный указатель (или нулевой указатель), при этом сохраняя право на владение объектом:
auto ptr = std::make_shared<B>(20);
B *pB = ptr.get();
std::cout << pB->y << std::endl; // 20
std::cout << ptr.use_count() << std::endl; // 1
swap()
— меняет местами значения двух объектов:
auto ptr1 = std::make_shared<B>(10);
auto ptr2 = std::make_shared<B>(20);
ptr1.swap(ptr2);
std::cout << ptr1->y << std::endl; // 20
std::cout << ptr2->y << std::endl; // 10
Вместо метода swap()
можно воспользоваться функцией swap()
:
auto ptr1 = std::make_shared<B>(10);
auto ptr2 = std::make_shared<B>(20);
std::swap(ptr1, ptr2);
std::cout << ptr1->y << std::endl; // 20
std::cout << ptr2->y << std::endl; // 10
reset()
— уменьшает значение счетчика на1
и обнуляет указатель в текущем объекте. Если значение счетчика будет равно0
, то освобождает динамическую память. Если в качестве параметра передан указатель, то «умный» указатель получает право собственности на него:
auto ptr1 = std::make_shared<B>(20); // B::B()
ptr1.reset(); // B::~B()
std::cout << ptr1.use_count() << std::endl; // 0
auto ptr2 = std::make_shared<B>(30); // B::B()
B *pB = new B(40); // B::B()
ptr2.reset(pB); // B::~B()
std::cout << ptr2.use_count() << std::endl; // 1
std::cout << ptr2->y << std::endl; // 40
// B::~B()
Для приведения типов можно воспользоваться следующими функциями:
static_pointer_cast()
— стандартное приведение типов (static_cast
). Прототип функции:
template<typename _Tp, typename _Up>
inline shared_ptr<_Tp>
static_pointer_cast(const shared_ptr<_Up> &sp) noexcept;
dynamic_pointer_cast()
— выполняет приведение типов указателей или ссылок (dynamic_cast
). Применяется для приведения полиморфных типов. Прототип функции:
template<typename _Tp, typename _Up>
inline shared_ptr<_Tp>
dynamic_pointer_cast(const shared_ptr<_Up> &sp) noexcept;
const_pointer_cast() — приведение const_cast. Прототип функции:
template<typename _Tp, typename _Up>
inline shared_ptr<_Tp>
const_pointer_cast(const shared_ptr<_Up> &sp) noexcept;
reinterpret_pointer_cast()
— приведениеreinterpret_cast
. Функция доступна, начиная со стандарта C++17. Прототип функции:
template<typename _Tp, typename _Up>
inline shared_ptr<_Tp>
reinterpret_pointer_cast(const shared_ptr<_Up> &sp) noexcept;
Управление массивом
Начиная со стандарта C++17, класс shared_ptr
можно также использовать для работы с динамическими массивами. Чтобы вызывался оператор delete
для массива нужно внутри угловых скобок после типа указать квадратные скобки:
std::shared_ptr<A[]> ptr(new A[2]);
ptr[0].x = 10;
ptr[1].x = 20;
std::cout << ptr[0].x << std::endl; // 10
std::cout << ptr[1].x << std::endl; // 20
Как видно из примера, для доступа к элементу массива используются квадратные скобки, внутри которых указывается индекс элемента внутри массива.
Помощь сайту
ЮMoney (Yandex-деньги): 410011140483022
ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов