0

所以,我已经完成了(少量)阅读,并且知道unique_ptr结合原始指针是建模唯一所有权时使用的模式。

但是,我真的很喜欢使用 weak_ptr 来检查一个值是否有效,然后在使用它之后丢弃 shared_ptr 的简单明了的概念,这让每个人都很高兴(以轻微的引用计数性能成本为代价)。

我现在的特殊问题是创建一个富有表现力和灵活的系统来跟踪多点触控点,使用代表触摸的对象的破坏作为触摸已经结束的信号似乎很优雅。如果我使用原始指针路由,我需要定义与该系统接口的每个组件都需要遵守的一些语义,这有点难看,比如涉及第二个参数,指示指针是否有效等。原始指针路由的这个问题可能是一个稻草人问题,因为我不认为这会成为一个大型项目,但就如何编写最好的现代 C++ 代码而言,这个问题主要是具有实际意义的。

伪代码:

class InputConsumer {
    void handle(std::list<std::weak_ptr<Touch>>*);
    // consumer doesnt hold references to anything outside of its concern. 
    // It only has to know how to deal with input data made available to it.
    // the consumer is a child who is given toys to play with and I am trying to
    // see how far I can go to sandbox it
}
class InputSender {
    std::list<std::weak_ptr<Touch>> exposedinputdata;
    std::list<std::shared_ptr<Touch>> therealownedtouches;
    // sender populates exposedinputdata when input events come in.
    // I want to let the consumer copy out weak_ptrs as much as it wants,
    // but for it to never hold on to it indefinitely. There does not appear
    // to be an easy way to enforce this (admittedly it is kind of vague. it
    // has to be around for long enough to be used to read out data, but
    // not e.g. 3 frames. Maybe what I need is to make an intelligent
    // smart pointer that has a timer inside of it.)
    std::list<std::weak_ptr<InputConsumer>> consumers;
    void feedConsumersWithInput() {
        for (auto i = consumers.begin(); i != consumers.end(); ++i) {
        if (i->expired()) {
            consumers.erase(i);
        } else {
            i->lock()->handle(&exposedinputdata);
        }
    }
}

当我看到weak_ptr表达与我正在建模的内容非常相似的语义的能力时,我真的很想使用它,因为它的界面简洁明了,最重要的是它自我记录了这段代码将如何工作。这是一个巨大的好处。

现在我很确定在调用InputConsumer并保留. 即使在它的主要所有者已经删除了它的所有权之后,它也会阻止底层资产被释放!在我看来,这似乎是唯一的皱纹,而且还有一点点。我认为用 shared_ptr 搞砸所有权处理要比用原始指针搞砸要难得多。lock()weak_ptr<Touch>shared_ptr<Touch>Touchshared_ptr

有什么方法可以解决这个问题?我正在考虑制作一个weak_ptr的模板子类(?!我从来没有写过这样的东西,最近进入了模板。爱他们)会以某种方式禁止保留shared_ptr或其他东西。

如果它不调用删除器,也许我可以shared_ptr继承并覆盖它的 dtor 来抛出?

4

2 回答 2

0

考虑到weak_ptr总是需要引用计数,推出任何解决方案(或多或少)就像重写shared_ptr一个。

快速而肮脏的方法可能是派生 shared_ptr 并仅为其提供移动 ctor ( monitore_ptr(monitored_ptr&&)) 和传输运算符 ( monitored_ptr& operator=(monitored_ptr&&)),从而禁用shared_ptr复制(以及因此“共享”)功能。

派生的问题是,shared_ptr不是多态的,你最终会得到一个非多态类型,它对 shared_ptr 表现出一些多态性(你可以分配给它,从而违反你的假设)。

这可以通过使用受保护的继承并仅重新公开所需的功能(本质上是*and -> 运算符)来弥补。

为了避免错误行为weak_ptr(比如你monitored_ptr给给weak_ptr给的shared_ptr)......我也建议weak_ptr用受保护的继承来覆盖。

那时,您最终会得到一对自给自足且与任何其他共享指针不兼容的类。

无论如何,关键是编写适当的构造函数,而不是(如您所建议的)抛出析构函数:这是一种有很多潜在问题的情况,很难管理。

(例如参见此处

于 2013-08-30T06:58:29.237 回答
0

我将提出一个非常简单的设计。它是一个瘦包装器,weak_ptr其中访问底层的唯一方法T是将 lambda 传递给方法。

这将shared_ptrfrom的生命周期限制为lock()您调用该方法的时间:虽然理论上您可以shared_ptr无限期锁定 from ,但您只能通过永不从try.

template<typename T>
struct monitored_pointer {
  template<typename Lambda>
  bool try( Lambda&& closure ) const {
    auto p = m_ptr.lock();
    if (!p)
      return false;
    std::forward<Lambda>(closure)(*p):
    return true;
  }
  bool valid() const {
    return try( [](T&){} );
  }
  void reset( std::weak_ptr<T> ptr = std::weak_ptr<T>() )
  {
    m_ptr = ptr;
  }
  explicit operator bool() const { return valid(); }
  monitored_pointer() {}
  monitored_pointer( monitored_pointer && ) = default;
  monitored_pointer& operator=( monitored_pointer && ) = default;
  explicit monitored_pointer( std::weak_ptr<T> ptr ):m_ptr(ptr) {}
private:
  std::weak_ptr<T> m_ptr;
};

valid并且operator bool在您想要清除过期monitored_pointer的 s 时提供帮助。

使用看起来像:

if (!ptr.try( [&]( Touch& touch ) {
  // code that uses the `touch` here
})) {
  // code that handles the fact that ptr is no longer valid here
}
于 2013-08-31T03:54:04.163 回答