использование умных указателей для создания одиночек [дубликат]

Вопрос или проблема

Я пытаюсь создать класс одиночка в C++. Для единственного экземпляра я не хочу использовать сырой указатель, вместо этого я предпочитаю использовать умные указатели, например так:

class A{
private:
    MyResource a;

private:
    A(): a(...){}
    ~A(){ a.release(); }

private:
    shared_ptr<A> S_INSTANCE;

public:
    static weak_ptr<A> GetInstance() {return S_INSTANCE;}
}
shared_ptr<A> A::S_INSTANCE = std::make_shared<A>();

Однако компилятор выдает ошибку, указывая, что A:A не может получить доступ к приватному члену. Думаю, это связано с тем, что умные указатели хотят использовать конструктор и деструктор класса A. Итак, вот мой вопрос:

1. Верно ли то, что я предполагаю?

2. Есть ли какое-либо решение для исправления этой проблемы (по-прежнему использовать умные указатели)?

Ответ или решение

Для построения паттерна Singleton в C++ с использованием умных указателей, необходимы четкие правила проектирования и управляемые ресурсы. Давайте рассмотрим ваш код и разберёмся в возникших проблемах, а также в том, как их исправить.

Анализ проблемы

Вы правы в вашем предположении, что ошибка компиляции связана с попыткой обращения к закрытому конструктору и деструктору класса A. Когда вы создаете статический экземпляр shared_ptr<A> с помощью std::make_shared<A>(), необходимо иметь доступ к конструктору класса A. Так как конструктор A является закрытым, вы не можете создать экземпляр класса напрямую вне его определения.

Ответы на ваши вопросы

  1. Ваше предположение верно. Ошибка возникает из-за того, что std::make_shared пытается вызвать закрытый конструктор класса A.

  2. Решение проблемы. Чтобы использовать умные указатели и сохранить паттерн Singleton, можно воспользоваться статическим методом для создания экземпляра класса, который будет использовать shared_ptr. Вот как можно переписать ваш класс A:

#include <memory>

class A {
private:
    // Ваши ресурсы
    int a; // Например, просто целое число в качестве примера

    // Закрытый конструктор
    A() : a(0) { /* инициализация */ }

    // Закрытый деструктор
    ~A() {}

    // Статический указатель на единственный экземпляр
    static std::shared_ptr<A> S_INSTANCE;

public:
    // Запрет копирования и оператор присваивания
    A(const A&) = delete;
    A& operator=(const A&) = delete;

    // Метод для получения экземпляра Singleton
    static std::shared_ptr<A> GetInstance() {
        if (!S_INSTANCE) {
            S_INSTANCE = std::shared_ptr<A>(new A());
        }
        return S_INSTANCE;
    }
};

// Инициализация *статического члена* класса
std::shared_ptr<A> A::S_INSTANCE = nullptr;

int main() {
    std::shared_ptr<A> instance = A::GetInstance();
    // Используйте instance по вашему усмотрению
    return 0;
}

Объяснение решения

  1. Закрытые члены класса: Мы должны обеспечить, чтобы конструктор и деструктор класса A оставались закрытыми для предотвращения создания экземпляров класса извне.

  2. Статический указатель shared_ptr: Мы создаем статический указатель S_INSTANCE, который будет хранить единственный экземпляр класса.

  3. Метод GetInstance: Этот метод проверяет, существует ли уже экземпляр. Если нет, он создает новый объект с помощью new и инициализирует shared_ptr. Использование new здесь допустимо, так как мы контролируем создание экземпляра и внутри класса.

  4. Запрет копирования: Для обеспечения уникальности экземпляра мы удаляем конструктор копирования и оператор присваивания.

Заключение

Такое решение позволяет использовать умные указатели для управления ресурсами класса Singleton в C++. Оно гарантирует, что только один экземпляр класса может существовать, и делает его более безопасным и управляемым. Умные указатели, такие как shared_ptr, позволяют избежать утечек памяти, благодаря автоматическому освобождению ресурсов при выходе из области видимости.

Если у вас есть дополнительные вопросы или требуется дальнейшее пояснение, не стесняйтесь обращаться!

Оцените материал
Добавить комментарий

Капча загружается...