У меня есть класс данных, который передается между потоками. Я хотел бы, чтобы этот класс был 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 Это подкласс, и деструктор можно вызвать из любого места в любом потоке, который имеет доступ к общему указателю и удалит базовый объект. Вот почему я хочу ограничить его.
I want the destructor to be private as well почему? В чем выигрыш? Если чем-то управляет shared_ptr, вы неявно передаете ответственность за уничтожение shared_ptr, поэтому я не понимаю, какой частный деструктор может что-то улучшить.
@MarekR Я использую подкласс QObject, и у него есть функция deleteLater(), которую я хочу отключить, скрыв деструктор.
QObject и shared_ptr? В вашем вопросе отсутствуют важные детали, и вы явно перепроектируете код.
@MarekR Да, QObject чтобы он мог иметь сигналы и слоты и shared_ptr чтобы его можно было легко использовать в разных потоках и использовать weak_ptr для хранения ссылки. Я мог бы также использовать QSharedPointer, но я предпочитаю shared_ptr. Я могу переусердствовать, но это центральный класс данных в моем приложении, который используется в потоках, поэтому я хочу быть особенно осторожным в отношении его жизненного цикла.
Не имеет отношения к пушке, но вам лучше получить от std::enable_shared_from_this<SharedOnly>, чтобы вы могли вызывать SharedOnly::shared_from_this и SharedOnly::weak_from_this по требованию. Потому что вы не хотели бы хранить this где бы то ни было.





Вы можете использовать лямбду для разрешения доступа к деструктору изнутри 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Моника хорошая идея, я попробовал, но почему-то не подумал просто повторно использовать средство удаления для unique_ptr...
Я не понимаю выгоды использования посредника std::unique_ptr?
@Chaitanya, это работает точно так же, но у меня нет необработанного указателя на владение, о котором можно было бы беспокоиться и сделать его безопасным для исключений. На самом деле, я просто облажался: конструктор std::shared_ptr указывает delete его аргумент в случае возникновения исключения, поэтому мой try/catch будет дважды удалять... :) Редактировать: это означает, что unique_ptr в конце концов бесполезен. Ну что ж.
Это не было бесполезно. Это заставило нас просмотреть контракты для различных конструкторов и доработать их до минимальной элегантной реализации.
Поскольку вы используете 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;
};
Чего вы хотите добиться, сохраняя деструктор закрытым?