Назад к вопросам
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[]).