Как использовать shared_ptr с пользовательским делегатом, не раскрывая деструктор

Вопросы и ответы

Наверное, это что-то очень тривиальное, но я не владею C++.

class hardwareMonitor : public hardwareMonitor_
{
public:
    // Перегруженные методы getInstance для отражения оригинальных конструкторов
    static std::shared_ptr<hardwareMonitor> getInstance();

    void cleanup();

    // Удаление конструктора копирования и оператора присваивания
    hardwareMonitor(const hardwareMonitor&) = delete;
    hardwareMonitor& operator=(const hardwareMonitor&) = delete;

private:
    // Приватные конструкторы, отражающие те, что в hardwareMonitor_
    hardwareMonitor() : hardwareMonitor_() {}

    // Деструктор
    ~hardwareMonitor() = default;

    // Указатель на единственный экземпляр класса
    static std::shared_ptr<hardwareMonitor> instance;
};

и реализация метода getInstance() (для создания экземпляра синглтона) с делетором:

std::shared_ptr<hardwareMonitor> hardwareMonitor::getInstance()
{
    if (instance == nullptr)
    {
        instance = std::make_shared<hardwareMonitor>(new hardwareMonitor(), [](hardwareMonitor * p) { p->cleanup(); });
    }
    return instance;
}

void hardwareMonitor::cleanup()
{
    delete this;
}

Но я получаю ошибку:

\include\xmemory(293,9): ошибка C2248: ‘hardwareMonitor::~hardwareMonitor’: нельзя получить доступ к закрытому члену класса ‘hardwareMonitor’

строка в xmemory:

        _Obj.~_Ty();

Возможно ли использовать shared_ptr с пользовательским делетором, не раскрывая деструктор?

Ответ

Чтобы использовать std::shared_ptr с пользовательским делетором, не раскрывая деструктор, нужно сделать некоторые изменения в вашей реализации. Ваша текущая реализация пытается использовать new для создания экземпляра класса, но std::make_shared не предназначен для использования с пользовательскими делеторами, так как он не позволяет контролировать, как объект будет уничтожаться.

Но, чтобы все работало корректно, вы можете использовать std::shared_ptr с кастомным делетором через лямбда-функцию или определение делетора. Вот как это можно сделать:

  1. Замените вызов std::make_shared на создание shared_ptr с использованием явного вызова new с пользовательским делетором.
  2. В качестве делетора просто вызовите cleanup.

Ниже приведён исправленный код:

#include <memory>

class hardwareMonitor_ {}; // Предположим, что это базовый класс

class hardwareMonitor : public hardwareMonitor_
{
public:
    // Методы для получения экземпляра
    static std::shared_ptr<hardwareMonitor> getInstance();

    void cleanup();

    // Удаляем конструктор копирования и оператор присваивания
    hardwareMonitor(const hardwareMonitor&) = delete;
    hardwareMonitor& operator=(const hardwareMonitor&) = delete;

private:
    // Приватный конструктор
    hardwareMonitor() : hardwareMonitor_() {}

    // Приватный деструктор
    ~hardwareMonitor() = default;

    // Указатель на единственный экземпляр класса
    static std::shared_ptr<hardwareMonitor> instance;
};

// Инициализация статического член-данного
std::shared_ptr<hardwareMonitor> hardwareMonitor::instance = nullptr;

std::shared_ptr<hardwareMonitor> hardwareMonitor::getInstance()
{
    if (instance == nullptr)
    {
        // Создаем shared_ptr с пользовательским делетором
        instance.reset(new hardwareMonitor(), [](hardwareMonitor* p) {
            p->cleanup(); // Вызываем cleanup
            delete p;     // Затем удаляем указатель
        });
    }
    return instance;
}

void hardwareMonitor::cleanup()
{
    // Здесь можем поместить логику очистки, если понадобится
}

Объяснение изменений:

  1. Использование reset: Вместо использования std::make_shared, мы используем reset, чтобы установить новый объект hardwareMonitor. Это позволяет нам явно указать делетор для управления уничтожением объекта.

  2. Удаление объекта в делеторе: Внутри делетора мы сначала вызываем метод cleanup(), а затем удаляем объект.

  3. Статический указатель: Мы инициализируем instance в самом начале, что гарантирует, что он будет иметь значение nullptr перед первым обращением.

Таким образом, при вызове getInstance() создается новый объект hardwareMonitor, и управление его жизненным циклом будет корректно обрабатываться с помощью std::shared_ptr, не exposing деструктор класса.

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

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