添加和删​​除项目而不使迭代器无效

发布于 2024-07-23 09:01:00 字数 1083 浏览 4 评论 0原文

我有一个包含“观察者”列表的对象。 这些观察者会收到有关情况的通知,并且他们可能会通过在对象中添加或删除自己或其他观察者来响应此更改。

我想要一种强大的、而不是不必要的缓慢的方式来支持这一点。

class Thing {
public:
    class Observer {
    public:
        virtual void on_change(Thing* thing) = 0;
    };
    void add_observer(Observer* observer);
    void remove_observer(Observer* observer);

    void notify_observers();
private:
    typedef std::vector<Observer*> Observers;
    Observers observers;
};

void Thing::notify_observers() {

    /* going backwards through a vector allows the current item to be removed in
    the callback, but it can't cope with not-yet-called observers being removed */
    for(int i=observers.size()-1; i>=0; i--)
        observers[i]->on_change(this);

// OR is there another way using something more iterator-like?

    for(Observers::iterator i=...;...;...) {
        (*i)->on_change(this); //<-- what if the Observer implementation calls add_ or remove_ during its execution?
    }
}

我也许可以有一个由 add_ 和 remove_ 设置的标志,以便在迭代器失效时重置它,然后每个观察者中可能有一个“生成”计数器,这样我就知道我是否已经调用了它?

I have an object that has a list of 'observers'. These observers get notified of things, and they might respond to this change by adding or removing themselves or other observers from the object.

I want a robust, and not unnecessarily slow, way to support this.

class Thing {
public:
    class Observer {
    public:
        virtual void on_change(Thing* thing) = 0;
    };
    void add_observer(Observer* observer);
    void remove_observer(Observer* observer);

    void notify_observers();
private:
    typedef std::vector<Observer*> Observers;
    Observers observers;
};

void Thing::notify_observers() {

    /* going backwards through a vector allows the current item to be removed in
    the callback, but it can't cope with not-yet-called observers being removed */
    for(int i=observers.size()-1; i>=0; i--)
        observers[i]->on_change(this);

// OR is there another way using something more iterator-like?

    for(Observers::iterator i=...;...;...) {
        (*i)->on_change(this); //<-- what if the Observer implementation calls add_ or remove_ during its execution?
    }
}

I could perhaps have a flag, set by add_ and remove_, to reset my iterator if it gets invalidated, and then perhaps a 'generation' counter in each observer so I know if I've already called it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

两人的回忆 2024-07-30 09:01:00

也许你可以使用更好的(?)设计。 例如,您可以让通知函数根据其返回值删除它们(或执行任何其他操作),而不是让观察者自行删除。

Maybe you could use a better(?) design. For example instead of having the Observers remove themselves, you could have the notify function remove them (or do any other operation) based on their return value.

一抹微笑 2024-07-30 09:01:00

添加或插入项目是否会使容器中的某些迭代器失效完全取决于容器类型。

您可能需要研究 std::list 因为这是对迭代器验证更宽容的容器之一。 例如,在删除元素时,只有指向已删除元素的迭代器才会失效。 所有其他迭代器仍然有效。

您仍然需要决定哪种操作是有效的。 您可以考虑不允许在观察者列表上直接添加/删除操作,并在发生通知时对添加和删除操作进行排队,在通知完成时对队列进行操作。

如果观察者只允许删除自己或添加新的观察者,这可能是矫枉过正,像这样的循环将是足够安全的:

for( std::list<Observer>::iterator i = observers.begin(); i != observers.end(); )
{
    std::list<Observer>::iterator save = i++;
    save->on_change();
}

Whether adding or inserting items will invalidate some are all iterators into a container is entirely dependent on the container type.

You may want to investigate std::list as this is one of the more tolerant containers with respect to iterator validation. For example, on removing an element, only iterators pointing at the removed element will be invalidated. All other iterators remain valid.

You still need to decide what sort of operations are valid. You could consider not allowing direct add/remove operations on the Observers list and queuing add and remove actions while a notify is occurring, actioning the queue on completion of the notify.

If observers are only allowed to remove themselves or add new observers this may be overkill and a loop such as this would be sufficiently safe:

for( std::list<Observer>::iterator i = observers.begin(); i != observers.end(); )
{
    std::list<Observer>::iterator save = i++;
    save->on_change();
}
羁〃客ぐ 2024-07-30 09:01:00

管理这种混乱的明智方法是使用一个标志,以便删除代码知道它是否正在迭代观察者。

在删除中,如果代码处于迭代中,则指针将设置为 null 而不是删除。 该标志被设置为第三状态以指示这已经发生。

观察者必须使用 [] 运算符进行迭代,以防在迭代期间调用 add 并重新分配数组。 数组中的空值将被忽略。

迭代后,如果设置标志以指示在迭代中删除了观察者,则可以压缩数组。

The sane way to manage this chaos is to have a flag so the remove code knows whether it's iterating the observers.

In the remove, if the code is in an iteration, then the pointer is set to null rather than removed. The flag is set to a third state to indicate that this has happened.

The observers must be iterated with [] operator in case an add is called during iteration, and the array is reallocated. Null values in the array are ignored.

After iteration, if the flag is set to indicate that observers were removed in the iteration, the array can be compacted.

随心而道 2024-07-30 09:01:00

拥有不会失效的迭代器的最简单方法是将观察者存储在列表中而不是向量中。 列表迭代器不会因添加或删除项目而失效,除非它们指向要删除的项目。

如果你想坚持使用向量,我能立即想到的最好方法是在添加一个项目时重置一个标志(添加可以使向量中的每个项目无效),然后使用预递减循环去通过向量(因为删除只会使该点之后的项目无效,而不会在该点之前)。

The simplest way to have iterators that won't be invalidated is to store your Observers in a list rather than in a vector. List iterators don't get invalidated by adding or removing items unless they are pointing to the item being removed.

If you want to stick with a vector, the best way I can think of straight away is to have a flag to reset if you add an item (adding can invalidate EVERY item in the vector) and then use a pre-decrement loop to go through the vector (as removing will only invalidate items after the point, never before it).

妄想挽回 2024-07-30 09:01:00

我认为你们几代人都走在正确的道路上。 您的问题不清楚的是观察者的变化是否需要应用于当前通知。 如果没有,那么我会将所有需要继续应用于下一代的观察者移动,并保留当前的迭代器。

I think you're on the right track with generations. What's not clear from your question is whether the change in observers needs to be applied to the current notification. If not, then I would move all observers that need to continue being applied to the next generation and leave the current iterator alone.

后知后觉 2024-07-30 09:01:00

如果不使任何指向或超出已删除项的迭代器无效,您就无法安全地从向量中添加和删除项。 如果这对您来说是个问题,也许您应该使用不同的容器? 您可以添加和删除列表或映射,仅使受影响位置的迭代器无效。

您可以使用以下方法进行迭代。 它允许在容器中任意插入和删除,因为我们正在制作副本:

void Thing::notify_observers()
{
   Observers obscopy=observers;
   Observers::iterator i=obscopy.begin();
   while (i!=obscopy.end())
   {
       (*i)->on_change(this);
       ++i;
   }
}

You cannot safely add and remove items from a vector without invalidating any iterators that are pointing at or beyond the item that you have removed. If this is a problem for you, perhaps you should use a different container? You can add and remove to a list or map, only invalidating the iterator at the position that was affected.

You could use the following method to iterate through. It allows arbitrary insertions and deletions in the container since we are making a copy:

void Thing::notify_observers()
{
   Observers obscopy=observers;
   Observers::iterator i=obscopy.begin();
   while (i!=obscopy.end())
   {
       (*i)->on_change(this);
       ++i;
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文