Создание класса только shared_ptr с частным деструктором?

У меня есть класс данных, который передается между потоками. Я хотел бы, чтобы этот класс был std::shared_ptr единственным классом, который нельзя уничтожить, вызвав его деструктор извне, и поэтому я хочу, чтобы деструктор также был закрытым. Мое текущее решение

template<typename T>
struct DestructorHelper
{
    static void Destroy(void* v) { delete static_cast<T*>(v); }
};

class SharedOnly
{
public:
    SharedOnly(const SharedOnly& other) = delete; // deleted copy constructor
    SharedOnly& operator=(const SharedOnly& other) = delete; // deleted copy assignment operator
    SharedOnly(SharedOnly&& other) = delete; // deleted move constructor
    SharedOnly& operator=(SharedOnly&& other) = delete; // deleted move assignment operator

    static std::shared_ptr<SharedOnly> create()
    {
        auto objPtr = new SharedOnly;
        auto desPtr = &DestructorHelper<SharedOnly>::Destroy;
        std::shared_ptr<SharedOnly> shared(objPtr, desPtr);
        return shared;
    }

private:
    SharedOnly() = default;
    ~SharedOnly() = default;
    friend struct DestructorHelper<SharedOnly>;
};

Могу ли я сделать этот класс без DestructorHelper? То, что я ищу, — это элегантное решение для создания единственного класса std::shared_ptr, чтобы его можно было передавать в разные потоки и уничтожать только тогда, когда уничтожается последний общий указатель, указывающий на объект.

Чего вы хотите добиться, сохраняя деструктор закрытым?

wohlstad 13.04.2023 11:21

@wohlstad Это подкласс, и деструктор можно вызвать из любого места в любом потоке, который имеет доступ к общему указателю и удалит базовый объект. Вот почему я хочу ограничить его.

Chaitanya 13.04.2023 11:26
I want the destructor to be private as well почему? В чем выигрыш? Если чем-то управляет shared_ptr, вы неявно передаете ответственность за уничтожение shared_ptr, поэтому я не понимаю, какой частный деструктор может что-то улучшить.
Marek R 13.04.2023 11:29

@MarekR Я использую подкласс QObject, и у него есть функция deleteLater(), которую я хочу отключить, скрыв деструктор.

Chaitanya 13.04.2023 11:34
QObject и shared_ptr? В вашем вопросе отсутствуют важные детали, и вы явно перепроектируете код.
Marek R 13.04.2023 12:07

@MarekR Да, QObject чтобы он мог иметь сигналы и слоты и shared_ptr чтобы его можно было легко использовать в разных потоках и использовать weak_ptr для хранения ссылки. Я мог бы также использовать QSharedPointer, но я предпочитаю shared_ptr. Я могу переусердствовать, но это центральный класс данных в моем приложении, который используется в потоках, поэтому я хочу быть особенно осторожным в отношении его жизненного цикла.

Chaitanya 13.04.2023 12:16

Не имеет отношения к пушке, но вам лучше получить от std::enable_shared_from_this<SharedOnly>, чтобы вы могли вызывать SharedOnly::shared_from_this и SharedOnly::weak_from_this по требованию. Потому что вы не хотели бы хранить this где бы то ни было.

Red.Wave 13.04.2023 14:10
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
122
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете использовать лямбду для разрешения доступа к деструктору изнутри create:

static std::shared_ptr<SharedOnly> create()
{
    return {new SharedOnly, [](SharedOnly *const s) { delete s; }};
}

Поднято за указание на то, что shared_ptr может сам выдать ошибку. Я думаю, вы можете перейти через unique_ptr, чтобы функция была немного более лаконичной и безопасной для исключений. Примерно так wandbox.org/permlink/5eolsIuwQG6SKQBg

StoryTeller - Unslander Monica 13.04.2023 12:25

@StoryTeller-UnslanderМоника хорошая идея, я попробовал, но почему-то не подумал просто повторно использовать средство удаления для unique_ptr...

Quentin 13.04.2023 14:04

Я не понимаю выгоды использования посредника std::unique_ptr?

Chaitanya 13.04.2023 14:51

@Chaitanya, это работает точно так же, но у меня нет необработанного указателя на владение, о котором можно было бы беспокоиться и сделать его безопасным для исключений. На самом деле, я просто облажался: конструктор std::shared_ptr указывает delete его аргумент в случае возникновения исключения, поэтому мой try/catch будет дважды удалять... :) Редактировать: это означает, что unique_ptr в конце концов бесполезен. Ну что ж.

Quentin 13.04.2023 15:03

Это не было бесполезно. Это заставило нас просмотреть контракты для различных конструкторов и доработать их до минимальной элегантной реализации.

StoryTeller - Unslander Monica 13.04.2023 15:55

Поскольку вы используете shared_ptr, то есть вы уже используете С++ 11 или выше, вы можете поместить свой помощник деструктора в лямбда-функцию.

class SharedOnly
{
public:
    SharedOnly(const SharedOnly& other) = delete; // deleted copy constructor
    SharedOnly& operator=(const SharedOnly& other) = delete; // deleted copy assignment operator
    SharedOnly(SharedOnly&& other) = delete; // deleted move constructor
    SharedOnly& operator=(SharedOnly&& other) = delete; // deleted move assignment operator

    static std::shared_ptr<SharedOnly> create()
    {
        auto objPtr = new SharedOnly;
        std::shared_ptr<SharedOnly> shared(objPtr, [](SharedOnly *ptr) {
            delete ptr;
        });
        return shared;
    }

private:
    SharedOnly() = default;
    ~SharedOnly() = default;

};

Другие вопросы по теме