Для чего нужен
shared_ptr нужен для ситуации, когда имеются несколько ссылок на объект, созданных в динамической памяти. При этом освобождать объект нужно тогда, когда удаляется последняя ссылка. И делать это нужно безопасно даже в многопоточном коде.
Как отслеживать ссылки на объект
Каждый объект shared_ptr содержит созданный в динамической памяти счётчик ссылок, который увеличивается на единицу, когда shared_ptr завладевает объектом, и уменьшается на единицу, когда shared_ptr перестаёт владеть объектом.
Код объекта счётчика ссылок может выглядеть так:
template <typename T>
class sp_counted {
public:
explicit sp_counted(T *p) noexcept : count(1), ptr(p) {}
void add_ref() noexcept {
++count;
}
void release() noexcept {
if (!--count) {
delete ptr;
delete this;
}
}
size_t use_count() const noexcept {
return count.load();
}
private:
std::atomic<size_t> count;
T *ptr;
};
При этом код shared_ptr может выглядеть так:
template <typename T>
class shared_ptr {
public:
shared_ptr() noexcept : ptr(nullptr), counted(nullptr) {}
// excaption safe конструктор
explicit shared_ptr(T *p) {
std::unique_ptr<T> holder(p);
// new может кинуть исключение, и, если p не передать в unique_ptr,
// память под p потеряется
counted = new sp_counted<T>(holder.get());
ptr = holder.release();
}
~shared_ptr() noexcept {
release();
}
shared_ptr(const shared_ptr &other) noexcept : ptr(other.ptr), counted(other.counted) {
add_ref();
}
shared_ptr &operator=(const shared_ptr &other) noexcept {
// Освобождаем владение предыдущим указателем
release();
// Выполняем присваивание
ptr = other.ptr;
counted = other.counted;
// Устанавливаем владение новым указателем
add_ref();
// Ура! Я не забыл вернуть *this!
return *this;
}
T *get() const noexcept {
return ptr;
}
size_t use_count() const noexcept {
return counted != nullptr ? counted->use_count() : 0;
}
private:
void add_ref() {
if (counted) {
counted->add_ref();
}
}
void release() {
if (counted) {
counted->release();
}
}
private:
T *ptr;
sp_counted<T> *counted;
};
А что с потокобезопасностью?
Любая операция с shared_ptr потокобезопасна, если каждый поток хранит копию shared_ptr. При этом каждая копия может хранить указатель на один объект.
Хранить ссылки на один shared_ptr в разных потоках непотокобезопасно.
Комментариев нет:
Отправить комментарий