Я не знаю, есть ли специальные ключевые слова для того, что мне нужно. Я пишу основную схема наблюдателя и я обеспокоен некоторыми проблемами. Моя реализация классическая. Я использую std :: set наблюдателей и всякий раз, когда мне нужно запустить событие, я перебираю этот набор и вызываю метод notify каждого из наблюдателей. Моя проблема заключается в следующем. Что происходит, когда наблюдаемые объекты отправляют событие наблюдателям, если:
Я знаю, что все эти случаи в конечном итоге произойдут. У меня есть идеи для третьего, но это не по теме. В первом и втором случае проблема заключается в том, что удаление или очистка std :: set приведет к аннулированию итератора, который я использую для перечисления наблюдаемых. Даже если это не так, наблюдаемое не должно уведомлять какого-либо наблюдателя, который был бы удален во время обработки события.
Я еще не нашел реализацию набора, который обеспечивает итератор, способный оставаться действительным, когда любой элемент удален. Тем не менее, было бы возможно реализовать это за счет некоторой косвенности и сохранения в контейнере любой ссылки на живой итератор, чтобы обновить его при необходимости.
Другое решение состоит в том, чтобы скопировать набор наблюдателя, выполнить итерацию копии и проверить, находится ли текущий итеративный наблюдатель в истинном наборе. (Это позволит забыть любого нового наблюдателя, добавленного во время мероприятия, но в этом случае мне все равно)
Есть ли у вас какие-либо предложения / решения по этой проблеме?
Я собираюсь предположить, что вы храните указатели или что-то в контейнере, а не сами объекты-наблюдатели. Потому что у вас явно не может быть кода наблюдателя, удаляющего объект наблюдателя!
std :: set просто нельзя использовать, когда вам нужно изменить набор, пока у вас есть итераторы. Это просто не то, для чего это.
Если вам нужно удалить вещи из контейнера, вы можете попытаться вернуть значение из подпрограммы события, которая сообщает вызывающей стороне (коду с итератором) удалить этот наблюдатель из контейнера. Этот код может удалить то, на что указывает итератор, и продолжить корректно по последовательности.
Если вам нужно добавить вещи, не добавляйте их непосредственно в контейнер. Вместо этого добавьте их в очередь, и код, запускающий события, добавит новый материал после завершения итерации по контейнеру.
Если в контейнере есть небольшое количество объектов, а объекты маленькие (указатели), я, вероятно, просто скопирую контейнер и переберу копию. Таким образом, наблюдатели могут испортить контейнер, сколько захотят, не облажая итераторы.
Пусть метод уведомления наблюдателя принимает итератор, который указывает на этого наблюдателя, и возвращает итератор, который указывает на следующего наблюдателя:
// PSEUDO-CODE not to be taken literally.
class normalObserver : public Observer {
iterator notify(iterator me) {
assert(*me == this);
// do stuff
return ++me;
}
};
class deleteMeObserver : public Observer {
iterator notify(iterator me) {
assert(*me == this);
// do stuff
object.observers.erase(me++);
return me;
}
};
class deleteEveryObserver :public Observer {
iterator notifiy(iterator me) {
assert(*me == this);
// do stuff
object.observers.clear();
return object.observers.end();
}
};
class Object {
set::set<Observer*> observers;
void notifyObservers() {
for(it = observers.begin(); it != observers.end(); ) {
it = (*it)->notify(it);
}
}
};
Я бы рекомендовал использовать std::list
вместо set
потому что таким образом итератор не будет признан недействительным при удалении элементов, если, конечно, он сам не удалился.
Вы можете обработать наблюдателя, желающего удалить себя, удерживая указатель (предпочтительно умный указатель или, возможно, даже слабый указатель) на объект наблюдателя вместо итератора (таким образом, когда он удаляет себя, указатель не будет признан недействительным (если он умный). и не удаляет себя так же))
Не предоставляйте наблюдателям прямой доступ к съемочной площадке. Позвольте им вносить изменения в метод, который имеет доступ как к множеству, так и к итератору, который должен быть увеличен до вызова наблюдателя. Этот метод может проверить, удаляется ли элемент в итераторе, и увеличить его, если это так. Для случая удаления всех, просто установите итератор в end()
,