【问题标题】:Check if container of shared_ptr contains a pointer?检查 shared_ptr 的容器是否包含指针?
【发布时间】:2011-11-08 18:46:13
【问题描述】:

使用观察者模式。我有一个类,例如 Monitor,它正在监视一组对象。该类是一个观察者,其集合中的每个对象都是一个主题。目前,该集合被实现为 shared_ptr 的 std::list。在 Monitor 类的 Update 方法中,我想检查更新是否来自其集合中的对象之一。

std::list<SomeSharedPointer> items_;
...
void Monitor::Update(Subject *subject)
{
    if(subject == something_)
    {
        DoSomething();
    }
    else if
    ??
    // if subject is one of the objects in our collection then do something..

}

这里的主题是一个原始指针,我的集合是一个 shared_ptr 列表。如何有效地检查进入的主题是否是我收藏中的任何一个对象?

(请注意我的编译器 msvc,如果有需要的算法解决方案,则支持 lambdas)

更新

我应该补充一点,我意识到我可以在容器上使用 for 循环,但我想知道是否有更时髦的方式。

更新 2

SomeSharedPointerstd::shared_ptr&lt;SomeType&gt; 的类型定义,其中SomeType 派生自抽象类Subject(标准观察者模式实现)。 SomeType 会在某个时候调用Notify(),这将为每个观察者调用Update() 方法。

【问题讨论】:

  • 听起来像是一个坏掉的设计。普通观察者不必进行检查。从长远来看,最好为该类型添加特殊的观察者
  • 有可能。它是一个需要根据其监控的项目状态做出决策的管理器类。

标签: c++ observer-pattern shared-ptr


【解决方案1】:
auto i = std::find_if(items_.begin(), items_.end(), 
    [=](const SomeSharedPointer& x) { return x.get() == subject; });

if (i != c.end())
{ 
    // Object found, and i is an iterator pointing to it
}

一个小辅助方法可以使这更具可读性:

typedef std::list<SomeSharedPtr> ObserverCollection;

// You can also add a const version if needed
ObserverCollection::iterator find_observer(Subject* s)
{
    return std::find_if(items_.begin(), items_.end(), 
        [=](const SomeSharedPointer& x) { return x.get() == s; });
}

然后,如果你需要迭代器,你可以这样使用它

auto i = find_observer(subject);
if (i != items_.end())
{
    // Object found
}

如果你不这样做,就直接这样:

if (find_observer(subject) != items_.end())
{
    ...
}

【讨论】:

  • 等待你的布尔辅助方法发生了什么?
  • @User:这取决于你想做什么。让我编辑帖子以将其取回。事实上,如果你想操作对象,你需要一个迭代器。
  • 还有 = 在 lambda 表达式中的 [=] 是什么意思?
  • [=] 表示通过值捕获在 lambda 范围之外引用的变量。
  • @User:[=] 用于捕获 lambda 中的 subject 指针。我们通过值来捕获它(我们可以通过 [&amp;] 引用来捕获它)。
【解决方案2】:

如果您没有 C++11 对 auto 的支持,请以老式方式声明迭代器

for (auto iter = items_.begin(); iter != items_.end(); ++iter)
{
     if (subject == iter->get())
     {
         .. do stuff ..
     }
}

共享指针有一个返回指针的.get()函数。

【讨论】:

  • 这是一个集合,而不是单个实例。
  • 抱歉,我以为你已经在迭代集合了。
【解决方案3】:

既然您说观察者需要根据它正在监视的项目的状态做出决定,那么您应该向基类添加一个方法(在您的问题中为Subject),该方法返回一个定义项目状态的枚举.然后根据状态,在更新方法中添加一个开关:

enum State{ STATE_1, STATE_2 };

void Monitor::Update(Subject *subject)
{
    switch( subject->getState() )
    {
      case STATE_1:
         // do something 1
         break;
      case STATE_2:
         // do something 2
         break;
      default:
         //error
    }
}

【讨论】:

  • 它需要根据聚合状态而不是任何一项的状态来做出决策。
【解决方案4】:

如果可能,您可以考虑将容器更改为可以提供更好搜索行为的容器。例如,您可以使用std::set。每次插入的成本更高,但每次查找的速度更快。或者,std::unordered_set。插入和查找都很快,但迭代可能更慢。为了实现正确的比较,您可以创建一个帮助类来将原始指针转换为具有无操作删除器的共享指针。

template <typename T>
struct unshared_ptr {
    std::shared_ptr<T> p_;
    unshared_ptr (T *p) : p_(p, [](...){}) {}
    operator const std::shared_ptr<T> & () const { return p_; }
    operator T * () const { return p_.get(); }
};

如果您的容器支持find 方法,那么:

typedef unshared_ptr<SomeType> unshared_some;
if (items_.end() != items_.find(unshared_some(subject))) {
    DoSomething();
}

Try it online!

如果你坚持使用std::list,你可以通过传入一个总是返回false但执行匹配测试的谓词来滥用remove_if方法。

bool matched = false;
auto pred = [subject, &matched](SomeSharedPtr &v) -> bool {
    if (!matched && v.get() == subject) {
        matched = true;
    }
    return false;
};
items_.remove_if(pred);
if (matched) {
    DoSomething();
} //...

【讨论】:

  • 哇!你从哪里得到这些想法?
  • @curiousguy:我以滥用 API 为生。
猜你喜欢
  • 2015-09-13
  • 1970-01-01
  • 1970-01-01
  • 2017-02-21
  • 2011-06-07
  • 2020-03-29
  • 2011-07-09
  • 1970-01-01
相关资源
最近更新 更多