shared_ptr/make_shared и делитер, довесить доп обработку
От: пффф  
Дата: 28.03.24 17:09
Оценка:
Привет!

Я плохо знаю про современные плюсики, изучаю методом тыка в основном

Насколько я знаю, можно создать shared_ptr, не заботясь о виртуальности деструктора — там есть делитер, который правильно вызывает деструктор конкретного класса, и тут есть возможность задать свой делитер.
При создании shared_ptr можно задать свой делитер, но если использовать make_shared — то вроде делитер уже нельзя задать. Или таки как-то можно?

Суть вопроса: у меня везде используется make_shared, и в какой-то момент мне понадобилось при удалении объекта произвести какие-то дополнительные действия. В раскладку объекта влезать нет возможности, чтобы что-то туда добавить, что могло бы управлять моим ресурсом, хочется иметь опциональную возможность навешивать доп действие при удалении объекта, которым рулит shared_ptr, или не навешивать, а использовать дефолтное разрушение.

Какие есть варианты?
Re: shared_ptr/make_shared и делитер, довесить доп обработку
От: Stanislav V. Zudin Россия  
Дата: 28.03.24 20:23
Оценка: 3 (1)
Здравствуйте, пффф, Вы писали:

П>Насколько я знаю, можно создать shared_ptr, не заботясь о виртуальности деструктора — там есть делитер, который правильно вызывает деструктор конкретного класса, и тут есть возможность задать свой делитер.

П>При создании shared_ptr можно задать свой делитер, но если использовать make_shared — то вроде делитер уже нельзя задать. Или таки как-то можно?


Кастомный делитер в make_shared не передать. Поэтому либо руками звать к-тор шаред-поинтера, либо сделать производный класс, в д-торе которого делать что тебе потребно. Тогда можно продолжать использовать make_shared.
_____________________
С уважением,
Stanislav V. Zudin
Re: shared_ptr/make_shared и делитер, довесить доп обработку
От: qaz77  
Дата: 28.03.24 21:20
Оценка:
Здравствуйте, пффф, Вы писали:
П>При создании shared_ptr можно задать свой делитер, но если использовать make_shared — то вроде делитер уже нельзя задать. Или таки как-то можно?
П>Какие есть варианты?

Я бы сделал свою функцию make_shared_my_del, которая возвращает std::shared_ptr с нужным делитером.
Реализация тривиальная с использованием std::forward.
Re: shared_ptr/make_shared и делитер, довесить доп обработку
От: watchmaker  
Дата: 28.03.24 21:32
Оценка: 2 (1) +1
Здравствуйте, пффф, Вы писали:


П>Насколько я знаю, можно создать shared_ptr, не заботясь о виртуальности деструктора — там есть делитер, который правильно вызывает деструктор конкретного класса, и тут есть возможность задать свой делитер.

Да

П>При создании shared_ptr можно задать свой делитер, но если использовать make_shared — то вроде делитер уже нельзя задать. Или таки как-то можно?


У make_shared одна из ролей (и главная оставшаяся в современном С++) — экономить число аллокаций.
И если make_shared выделяет в памяти один блок, который хранит и счётчик, и объект, то и для удаления такого блока нужно иметь определённый deleter, который знает как его удалять.
А если ты хочешь добавить ещё и свой deleter, то получается у объекта будут сразу два разных deleter'a? Как ты себе такое представляешь? Такое вообще никогда работать не будет: ни с shared_ptr, ни в других ситуациях.

П>Суть вопроса: у меня везде используется make_shared, и в какой-то момент мне понадобилось при удалении объекта произвести какие-то дополнительные действия. ... хочется иметь опциональную возможность навешивать доп действие при удалении объекта, которым рулит shared_ptr, или не навешивать, а использовать дефолтное разрушение.


Не нужен тебе для этого deleter. Делай свои действия в деструкторе, или в scope_exit или в аналоге.



П> В раскладку объекта влезать нет возможности, чтобы что-то туда добавить, что могло бы управлять моим ресурсом


Не нужно в объект влезать. Положи свои действия в обёртке рядом с ним.
Да даже сам make_shared<T> точно так же поступает: он же не влезает в раскладку объекта T чтобы добавить в него интрузивные счётчики, а вместо этого (обычно) просто делает структуру, в которой хранятся в соседних полях и контрольный блок, и пользовательский объект T. И создаёт уже такую структуру в памяти. А потом пользователю возвращает указатель на подполе этой структуры, где объект типа T лежит.
Делай точно так же.
scope_exit и make_shared можно свободно комбинировать — они ортогональны.
И aliasing constructor тебе в помощь.
Отредактировано 28.03.2024 21:34 watchmaker . Предыдущая версия .
Re: shared_ptr/make_shared и делитер, довесить доп обработку
От: andrey.desman  
Дата: 28.03.24 23:47
Оценка: 2 (1)
Здравствуйте, пффф, Вы писали:

П>Насколько я знаю, можно создать shared_ptr, не заботясь о виртуальности деструктора — там есть делитер, который правильно вызывает деструктор конкретного класса, и тут есть возможность задать свой делитер.

П>При создании shared_ptr можно задать свой делитер, но если использовать make_shared — то вроде делитер уже нельзя задать. Или таки как-то можно?

Можно, но только вместе с алокатором. Используй std::allocate_shared.
Re: shared_ptr/make_shared и делитер, довесить доп обработку
От: andrey.desman  
Дата: 29.03.24 00:25
Оценка: 2 (1)
Здравствуйте, пффф, Вы писали:

П>Какие есть варианты?


Конкретно про враппер и aliasing constructor тебе выше рекомендуют примерно такое:

#include <iostream>
#include <memory>

template<typename T>
struct object_wrapper {
    template<typename... Args>
    object_wrapper(Args&&... args): t(std::forward<Args>(args)...) { }
    ~object_wrapper() {
        std::cout << "~object_wrapper()\n";
        // do whatever you want
    }
    T t;
};

template<typename T, typename... Args>
std::shared_ptr<T> make_wrapped_shared(Args&&... args) {
    auto r = std::make_shared<object_wrapper<T>>(std::forward<Args>(args)...);
    return std::shared_ptr<T>(r, &r->t);
}

struct unchangeable {
    unchangeable() { std::cout << "unchangeable()\n"; }
    ~unchangeable() { std::cout << "~unchangeable()\n"; }

    void foo() { std::cout << "foo() a=" << a << '\n'; }

    int a = 12345;
};

int main() {
    auto s = make_wrapped_shared<unchangeable>();
    s->foo();
    return 0;
}


unchangeable()
foo() a=12345
~object_wrapper()
~unchangeable()
Re: shared_ptr/make_shared и делитер, довесить доп обработку
От: Chorkov Россия  
Дата: 29.03.24 10:01
Оценка: 2 (1)
Здравствуйте, пффф, Вы писали:

П>Какие есть варианты?


Можно унаследоваться от требуемого класса, (или включить класс в другой в качестве члена класса), и выполнить действия в производном классе.

С другой стороны, если не жалко лишнею аллокацию, то можно прицепить мониторинг вызова деструктора к уже существующему умному указателю.

template<class A, class OnExit>
std::shared_ptr<A>
monitor_pointer_lifetime( std::shared_ptr<A> ptr, OnExit on_exit )
{
    A* raw_ptr = ptr.get();
    auto deleter=[ptr=std::move(ptr), on_exit=std::move(on_exit)](void*) { on_exit(); };   
    return std::shared_ptr<A>( raw_ptr, std::move(deleter) );
}


пример

Я таким образом искал утечку умных указателей, прикручивая счетчики числа живых объектов, выданных разными способами.
Отредактировано 29.03.2024 10:17 Chorkov . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.