Регистрация слабого наблюдателя в конструкторе - PullRequest
0 голосов
/ 11 декабря 2018

Я пытаюсь переписать нашу реализацию Observer / Observable, чтобы использовать std :: shared_ptr / std :: weak_ptr, чтобы избавиться от некоторых неприятных состояний гонки, присутствующих в коде.

Как правило, регистры-наблюдатели регистрируютсясами, когда выполняется какое-то условие или когда они конструируют дочерние объекты, например, так:

// Used to be raw 'this' now child instead derives a weak_ptr and stores it
child->addObserver(shared_from_this()) 

И отменяет регистрацию в деструкторе, например:

child->removeObserver(this); // Not shared_from_this() since in destructor

В некоторых ситуациях это работает нормально, однаково многих случаях наблюдатель хочет зарегистрировать себя в конструкторе.Поскольку shared_ptr еще не создан, мы не можем вызывать shared_from_this ().

Поскольку для реализации шаблона наблюдателя в C ++ обычно рекомендуется weak_ptr, мне интересно, каков идиоматический способ решения вышеуказанной проблемы.

Некоторые мысли:

  • Пусть фабрика, которая создает объект-наблюдатель, регистрирует наблюдателя.Это приводит к утечке абстракций у наблюдателя (почему фабрика должна знать, кого хочет наблюдать ребенок?) И вынуждает наблюдателя выставлять внутренние объекты, которые он может захотеть наблюдать
  • Добавить метод init, который вызывается фабрикой после конструкторазавершено, лучше, чем выше, но какова семантическая разница между конструктором и init в любом случае?Что должно быть сделано где?Это даже RAII?Действительно, некоторые языки даже называют свои конструкторы init.
  • Передать лямбду в конструктор, который принимает другую лямбду, которая вызывается после построения
  • Может быть, какая-то магия шаблона?
  • Реализовать шаблон наблюдателя другим способом.

1 Ответ

0 голосов
/ 12 декабря 2018

Один из способов справиться с проблемой, о которой вы ее просили, - это создать явный экземпляр объекта-наблюдателя, содержащий shared_ptr и содержащийся в «родительском объекте».Объект-наблюдатель будет отправлять наблюдения родительскому объекту.

Однако, поскольку дочерний элемент регистрирует shared_ptr в weak_ptr, родительскому объекту фактически нет необходимости явно удалять себя в качестве наблюдателя.Когда ребенок отправляет уведомления наблюдателям, он проверяет, действителен ли сначала weak_ptr.Если он больше не действителен, он может удалить наблюдателя на месте вместо уведомления.

void notify_observers (Event e) const {
    auto o = observers_.begin();
    auto erase = [this](decltype(o) o) {
        return observers_.erase(o);
    };
    while (o != observers_.end()) {
        if (auto l = o->lock()) ++o, l->notify(e);
        else o = locked_call(erase, o);
    }
}

Попробуйте онлайн!

...