Назад к вопросам
Middle+
83
questionbank
Как написать умный указатель для одного или нескольких объектов?
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
#include <atomic>
#include <utility>
// Простой умный указатель для одного объекта (аналог std::unique_ptr)
template <typename T>
class UniquePointer {
public:
// Конструктор, принимающий сырой указатель
explicit UniquePointer(T* ptr = nullptr) : ptr_(ptr) {}
// Деструктор
~UniquePointer() {
delete ptr_;
}
// Запрещаем копирование и присваивание
UniquePointer(const UniquePointer&) = delete;
UniquePointer& operator=(const UniquePointer&) = delete;
// Разрешаем перемещение
UniquePointer(UniquePointer&& other) noexcept : ptr_(other.ptr_) {
other.ptr_ = nullptr;
}
UniquePointer& operator=(UniquePointer&& other) noexcept {
if (this != &other) {
delete ptr_;
ptr_ = other.ptr_;
other.ptr_ = nullptr;
}
return *this;
}
// Доступ к объекту
T* operator->() const { return ptr_; }
T& operator*() const { return *ptr_; }
// Получение сырого указателя
T* get() const { return ptr_; }
// Отсоединение указателя
T* release() {
T* temp = ptr_;
ptr_ = nullptr;
return temp;
}
// Замена сырого указателя
void reset(T* ptr = nullptr) {
if (ptr_ != ptr) {
delete ptr_;
ptr_ = ptr;
}
}
// Проверка на нулевой указатель
operator bool() const { return ptr_ != nullptr; }
private:
T* ptr_; // Сырой указатель
};
// Простой умный указатель для одного или нескольких объектов с подсчетом ссылок (аналог std::shared_ptr)
template <typename T>
class SharedPointer {
private:
T* ptr_; // Сырой указатель на объект(ы)
std::atomic<int>* ref_count_; // Указатель на счетчик ссылок (атомарный для потокобезопасности)
public:
// Конструктор по умолчанию
SharedPointer() : ptr_(nullptr), ref_count_(nullptr) {}
// Конструктор, принимающий сырой указатель
explicit SharedPointer(T* ptr) : ptr_(ptr), ref_count_(new std::atomic<int>(1)) {}
// Конструктор копирования
SharedPointer(const SharedPointer& other) : ptr_(other.ptr_), ref_count_(other.ref_count_) {
if (ref_count_) {
(*ref_count_)++; // Увеличиваем счетчик ссылок
}
}
// Оператор присваивания копированием
SharedPointer& operator=(const SharedPointer& other) {
if (this != &other) {
// Уменьшаем счетчик для текущего указателя перед переприсваиванием
decrement_ref();
ptr_ = other.ptr_;
ref_count_ = other.ref_count_;
// Увеличиваем счетчик для нового указателя
if (ref_count_) {
(*ref_count_)++;
}
}
return *this;
}
// Конструктор перемещения
SharedPointer(SharedPointer&& other) noexcept : ptr_(other.ptr_), ref_count_(other.ref_count_) {
other.ptr_ = nullptr;
other.ref_count_ = nullptr;
}
// Оператор присваивания перемещением
SharedPointer& operator=(SharedPointer&& other) noexcept {
if (this != &other) {
// Уменьшаем счетчик для текущего указателя перед переприсваиванием
decrement_ref();
ptr_ = other.ptr_;
ref_count_ = other.ref_count_;
other.ptr_ = nullptr;
other.ref_count_ = nullptr;
}
return *this;
}
// Деструктор
~SharedPointer() {
decrement_ref();
}
// Доступ к объекту
T* operator->() const { return ptr_; }
T& operator*() const { return *ptr_; }
// Получение сырого указателя
T* get() const { return ptr_; }
// Получение количества ссылок
long use_count() const {
if (ref_count_) {
return ref_count_->load();
}
return 0;
}
// Проверка на нулевой указатель
operator bool() const { return ptr_ != nullptr; }
private:
// Вспомогательная функция для уменьшения счетчика ссылок и очистки памяти
void decrement_ref() {
if (ref_count_) {
// Атомарное уменьшение счетчика и проверка, стал ли он равным нулю
if (--(*ref_count_) == 0) {
delete ptr_; // Удаляем объект(ы)
delete ref_count_; // Удаляем счетчик
ptr_ = nullptr;
ref_count_ = nullptr;
}
}
}
};
// Специализация для массива (для SharedPointer)
template <typename T>
class SharedPointer<T[]> {
private:
T* ptr_; // Сырой указатель на массив объектов
std::atomic<int>* ref_count_; // Указатель на счетчик ссылок (атомарный)
public:
// Конструктор, принимающий сырой указатель на массив
explicit SharedPointer(T* ptr) : ptr_(ptr), ref_count_(new std::atomic<int>(1)) {}
// Конструктор копирования
SharedPointer(const SharedPointer& other) : ptr_(other.ptr_), ref_count_(other.ref_count_) {
if (ref_count_) {
(*ref_count_)++;
}
}
// Прочие конструкторы и операторы аналогичны SharedPointer для одного объекта,
// но деструктор должен использовать `delete[]`.
// Деструктор для массива
~SharedPointer() {
if (ref_count_) {
if (--(*ref_count_) == 0) {
delete[] ptr_; // Удаляем массив объектов
delete ref_count_;
ptr_ = nullptr;
ref_count_ = nullptr;
}
}
}
// Доступ к элементам массива
T& operator[](size_t index) const { return ptr_[index]; }
// Получение сырого указателя
T* get() const { return ptr_; }
// Получение количества ссылок
long use_count() const {
if (ref_count_) {
return ref_count_->load();
}
return 0;
}
// Проверка на нулевой указатель
operator bool() const { return ptr_ != nullptr; }
};
Умные указатели для одного объекта (например, UniquePointer):
- Управление владением: Указатель является исключительным владельцем объекта.
- Автоматическое удаление: Объект автоматически удаляется при выходе указателя из области видимости или при переприсваивании.
- Нет подсчета ссылок: overhead минимален.
- Неявная передача владения: При перемещении ownership передается без копирования объекта.
Умные указатели для одного или нескольких объектов с подсчетом ссылок (например, SharedPointer):
- Совместное владение: Несколько указателей могут владеть одним и тем же объектом.
- Автоматическое удаление: Объект удаляется только тогда, когда количество ссылок становится равным нулю.
- Использует счетчик ссылок: overhead больше из-за управления счетчиком.
- Потокобезопасность (при использовании
std::atomic): Счетчик ссылок атомарный для безопасного использования в многопоточных приложениях. - Возможность управления массивами: Может быть специализирован для корректного удаления массивов (
delete[]).