Как я могу принудить единоличное владение с weak_ptr? (Или, как создать подкласс shared_ptr и переопределить dtor)

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

Тем не менее, мне очень нравится простая и понятная концепция использования weak_ptr, чтобы проверить, является ли значение допустимым, а затем отбросить shared_ptr после его использования, и это радует всех (при небольшом снижении производительности подсчета ссылок).

Моя конкретная проблема сейчас заключается в создании выразительной и гибкой системы для отслеживания точек с несколькими касаниями, и было элегантно использовать уничтожение объекта, представляющего прикосновение, в качестве сигнала о завершении касания. Если бы я пошел с необработанным маршрутом указателя, мне нужно было бы определить некоторую семантику, которой должен был бы соответствовать каждый компонент, взаимодействующий с этой системой, что-то немного уродливое, например, наличие второго аргумента, который указывает, действителен ли указатель или что-то подобное.
Эта проблема с маршрутом необработанного указателя, возможно, является проблемой, так как я не ожидаю, что это станет большим проектом, но вопрос в основном представляет практический интерес с точки зрения того, как написать лучший современный код C ++.

псевдокод:

class InputConsumer {
void handle(std::list<std::weak_ptr<Touch>>*);
// consumer doesnt hold references to anything outside of its concern.
// It only has to know how to deal with input data made available to it.
// the consumer is a child who is given toys to play with and I am trying to
// see how far I can go to sandbox it
}
class InputSender {
std::list<std::weak_ptr<Touch>> exposedinputdata;
std::list<std::shared_ptr<Touch>> therealownedtouches;
// sender populates exposedinputdata when input events come in.
// I want to let the consumer copy out weak_ptrs as much as it wants,
// but for it to never hold on to it indefinitely. There does not appear
// to be an easy way to enforce this (admittedly it is kind of vague. it
// has to be around for long enough to be used to read out data, but
// not e.g. 3 frames. Maybe what I need is to make an intelligent
// smart pointer that has a timer inside of it.)
std::list<std::weak_ptr<InputConsumer>> consumers;
void feedConsumersWithInput() {
for (auto i = consumers.begin(); i != consumers.end(); ++i) {
if (i->expired()) {
consumers.erase(i);
} else {
i->lock()->handle(&exposedinputdata);
}
}
}

Когда я увидел способность weak_ptr чтобы выразить семантику, очень похожую на то, что я моделирую, я просто очень хотел ее использовать, потому что ее интерфейс прост и понятен, и что наиболее важно, он самодокументирует, как этот код будет работать. Это огромное преимущество в будущем.

Теперь я почти уверен, что все будет очень хорошо до тех пор, пока InputConsumer звонки lock() на weak_ptr<Touch> и сохраняет shared_ptr<Touch>, Это предотвратит Touch от освобождения даже после того, как его основной владелец стер shared_ptr! Мне кажется, это единственная морщинка, причем маленькая. Я думаю, что гораздо сложнее испортить обработку владения с помощью shared_ptr, чем с необработанными указателями.

Какие есть способы исправить это? Я подумываю о создании подкласса шаблона (?! Я никогда не писал такую ​​вещь, недавно вошел в шаблоны. Люблю их) из weak_ptr, который каким-то образом запрещает сохранять shared_ptr, или что-то в этом роде.

Может быть, я могу подкласс shared_ptr и переопределить его dtor, чтобы бросить, если он не вызывает удаление?

0

Решение

Учитывая, что имея weak_ptr всегда требует подсчета ссылок, развертывания любого решения (более или менее), как переписывание shared_ptr один.

Быстрый и грязный способ, вероятно, является производным shared_ptr и предоставление ему только ctor хода (monitore_ptr(monitored_ptr&&) ) и оператор передачи (monitored_ptr& operator=(monitored_ptr&&) ), таким образом отключая shared_ptr копировать (и, следовательно,разделениевозможности.

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

Это можно компенсировать, используя защищенное наследование и повторно раскрывая только необходимые функции (по существу, * а также -> операторы).

Чтобы избежать неправильного поведения против weak_ptr (как ваш monitored_ptr дано weak_ptr дано shared_ptr) … я тоже предлагаю переопределить weak_ptr а также с защищенным наследованием.

На этом этапе вы получите пару классов, которые являются самодостаточными и несовместимыми с любым другим указателем общего доступа.

В любом случае, ключом является написание правильных конструкторов, а не (как вы предложили) добавление деструктора: это ситуация с большим потенциалом, трудно управляемым.

(см. например Вот)

0

Другие решения

Я собираюсь предложить довольно простой дизайн. Это тонкая обертка вокруг weak_ptr где единственный способ получить доступ к базовому T это передать лямбду в метод.

Это ограничивает срок службы shared_ptr от lock() время вызова метода: теоретически вы можете заблокировать shared_ptr до бесконечности, вы можете сделать это только никогда не возвращаясь из try,

template<typename T>
struct monitored_pointer {
template<typename Lambda>
bool try( Lambda&& closure ) const {
auto p = m_ptr.lock();
if (!p)
return false;
std::forward<Lambda>(closure)(*p):
return true;
}
bool valid() const {
return try( [](T&){} );
}
void reset( std::weak_ptr<T> ptr = std::weak_ptr<T>() )
{
m_ptr = ptr;
}
explicit operator bool() const { return valid(); }
monitored_pointer() {}
monitored_pointer( monitored_pointer && ) = default;
monitored_pointer& operator=( monitored_pointer && ) = default;
explicit monitored_pointer( std::weak_ptr<T> ptr ):m_ptr(ptr) {}
private:
std::weak_ptr<T> m_ptr;
};

valid а также operator bool просто помогает когда хочешь убрать истекший monitored_pointers.

Использование выглядит примерно так:

if (!ptr.try( [&]( Touch& touch ) {
// code that uses the `touch` here
})) {
// code that handles the fact that ptr is no longer valid here
}
0

По вопросам рекламы [email protected]