使用std :: unordered_set的多个主题的观察者

时间:2015-09-05 11:28:37

标签: c++ observer-pattern

我已经看到了Observer设计模式的实现,其中Observer负责多个Subject。大多数这些实现都使用std::vector<Subject*>来跟踪主题。

我是否可以使用std::unordered_set<weak_ptr<Subject>>做类似的事情?

我想使用unordered_set的原因是我不需要重复,而且我不需要有序的容器。根据我的理解,unordered_set是这种情况下的方法。另外,我使用weak_ptr的原因是它应该更安全吗?

如果您不同意,请留下答案,说明我应该使用哪个容器。如果我确实使用了unordered_set,我必须为weak_ptr声明一个哈希函数,但这可以通过使用内部指针的哈希函数来实现,用subjects.lock().get()获得?

5 个答案:

答案 0 :(得分:1)

首先,在我的回答中,我将使用Subject作为向注册观察者发送消息的人,因为这是这两个术语的常见用法。

  

我是否可以使用std::unordered_set<weak_ptr<Observer>>做类似的事情?

有可能。但是请记住,weak_ptr保存的对象可以被释放,weak_ptr需要在访问底层对象之前被转换为shared_ptr。这样做是因为在处理对象时不会释放对象。

  

使用std :: unordered_set&gt;是否可以做类似的事情?代替?

如果您需要强制执行唯一性,则unordered_set对我来说是一个不错的选择。如果你不需要,那么矢量就是更简单的解决方案。有人会告诉你,unique_set比矢量要慢,需要更多的内存,但是除非你需要观察者或其中数千个频率的高频率注册,否则你不会注意到差异。

关于弱指针,它为您提供了在注册时释放观察者的灵活性,所以应该没问题。如果您来自Java等内存管理语言,则此行为可能会出乎意料。如果你想在他们在你的主题中注册时保持它们存在,你可以使用shared_pointer。

  

我必须为weak_ptr声明一个哈希函数,但是这可以通过使用使用observer.lock()得到的指针内部的哈希函数来实现.get()?

创建哈希函数时要小心,我不建议您使用对象的指针作为哈希函数,特别是如果可以复制/移动主题。相反,您可以在使用计数器创建时为每个主题创建唯一标识符,并记住相应地编写复制/移动构造函数和运算符。

如果你不能写一个识别哈希函数,那么你就不应该使用unique_set,因为你失去了它带来的好处。

作为一个脚注,对象容器的美妙之处在于您可以根据自己的需要调整它们,如果它能够满足您的需求,那么每个解决方案都是正确的解决方案。

答案 1 :(得分:0)

在Observer模式中,Observer订阅有关Subject的更改的通知。主体负责在其可观察状态发生变化时更新所有已订阅的观察者。为了工作,观察者不需要跟踪主题。相反,主体必须跟踪所有订阅的观察者。

可以在此处找到Observer模式的一个很好的解释:https://sourcemaking.com/design_patterns/observer

代码大纲:

class Subject;

class Observer
{
public:
    // when notified about a change, the Observer
    // knows which Subject changed, because of the parameter s
    virtual void subjectChanged(Subject* s)=0;

};

class Subject
{
private:
    int m_internalState;
    std::set<Observer*> m_observers;

public:
    void subscribe(Observer* o)
    {
        m_observers.insert(o);
    }

    void unsubscribe(Observer* o);
    {
        m_observers.erase(o);
    }

    void setInternalState(int state)
    {
        auto end=m_observers.end();
        for(auto it=m_observers.begin(); it != end; ++it)
            it->subjectChanged(this);
    }

};

在大多数情况下,选择哪种精确的集合类型来存储观察者并不重要,因为观察者很少。但是,选择set-type有一个好处,即每个Observer只会收到一个通知。使用vector,如果(出于某种原因)多次订阅,则可能会发生相同的观察者收到有关同一更改的多个通知。

答案 2 :(得分:0)

真的没有一个正确的&#39;回答容器的选择;从性能的角度来看,这取决于你的目标。而且表现是否真的对此非常重要。

它还取决于记忆效率。如果您只有一些这些unordered_set对象并且需要非常快速的查找,那么它可能是一个不错的选择。由于它是一个哈希表,因此每个unordered_set对象将使用相当大量的内存。

如果你有很多物品相当少的unordered_set对象,那么在内存预算方面可能会有点贵。如果你需要快速插入和删除,那么在这种情况下std :: set可能会更好。但是,如果集合只包含少量项目,那么由于处理器缓存而导致std :: vector的线性搜索实际上查找速度可能会更快(即与std :: set相比,矢量元素的引用位置更好 - 这可能导致更多元素位于同一缓存行上。 vector的内存使用率将低于std :: set或std :: unordered_set。

如果由于某种原因需要快速查找特定对象并使用std :: vector并且通常具有适度数量的元素,那么您可以按排序顺序将项目插入到向量中。然后,您可以使用std :: lower_bound执行O(log n)二进制搜索查找。但是,插入和删除元素的成本可能很高。

在大多数情况下,我可能只是去std:vector - 你通常只有很少的观察者,所以也可以保持内存使用更紧密。 如果在其他地方使用shared_ptr,那么使用weak_ptr肯定是一个不错的选择。

答案 3 :(得分:0)

我真的认为使用std::unordered_set有点过分杀人 这个观察者模式是什么?当一个事件或状态发生变化时,迭代一系列状态检查器,如果状态无效或任何类型的特殊状态,则让它们做某事。

这就是说,你想用带有覆盖虚函数的对象迭代一个数组并调用它。为什么set给我们带来任何好处? 另外,我没有得到weak_ptr的想法 - 观察者的所有者是拥有它们的数组。该数组的所有者是主题。

现在所有人都说了,我会选择std::vector<std::unique_ptr<Observer>>

编辑:

使用C ++ 11,我甚至可以使用std::vector<std::function<void(Subject&)>>并避免继承+覆盖的样板。

答案 4 :(得分:-2)

最简单的方法是使用已经为您实现此功能的boost::signals2滚动所有签名。您的方法的根本问题在于,实现与特定主题和观察者的特定签名相关联,与适用于所有情况的通用解决方案相比,这实际上毫无价值。

观察者模式不是模式,它是一个类模板。