【问题标题】:Is there a weak_ptr equivalent to shared_from_this?是否有等效于 shared_from_this 的weak_ptr?
【发布时间】:2016-11-12 01:31:57
【问题描述】:

我知道一个班级将永远std::shared_ptr所有。然而,将shared_ptr 甚至weak_ptr 传递给不需要所有权或生命周期保证的函数和方法会产生不必要的开销。为了解决这个问题,我经常将原始指针传递给函数。类本身继承自std::enable_shared_from_this,因此如果函数需要获取指针的所有权,它可以使用类的方法来获取shared_ptr

这一切都很好。但是在某些情况下,我真的不想从原始指针创建 shared_ptr,而我想要的是 weak_ptr

根据我对std::shared_ptr 的通常实现的理解,它有两个用作引用计数器的原子变量;一张给shared_ptr,一张给weak_ptr

如果我只有一个指向我的类的原始指针并且我想要一个weak_ptr,我必须首先创建一个shared_ptr 并转换它。这样做意味着引用计数器会像这样改变:

  • 构造shared_ptr,增加shared_ptr计数器
  • 复制构造weak_ptr,递增weak_ptr 计数器
  • 允许shared_ptr 超出范围,递减shared_ptr 计数器

这似乎违背了“不用为不用的东西付费”的理念。有没有办法让我的班级只提供weak_ptr 而无需先创建shared_ptr

【问题讨论】:

  • 这更像是您需要 shared_from_this 而不是 enable_shared_from_this。类似weak_from_this.
  • 你是对的,我会编辑标题。有一个为 C++17 提议的weak_from_this,但我正在寻找我现在可以使用的东西。
  • 这意味着该类只使用std::make_shared<T>() 创建。它们总是比我传递原始指针的范围更持久。如果原始指针需要存储在它传递到的范围之外,它会被转换回shared_ptr
  • @Fibbles:“这似乎违背了“你不用为不用的东西付费”的想法。” 从最技术的意义上说,是的.但是,尽管有这句格言,C++ 标准有许多 地方可以让你为不使用的东西付费。而且我不只是在谈论 iostream。在这种情况下,成本非常低(3 个原子计数器更改),可能不值得担心。
  • @Fibbles:其实shared_ptr本身就是这样一个对象。它在get 返回的对象和它管理的对象的生命周期之间存在区别。 Many people never actually use that functionality, 但他们为此付出了代价,因为这就是为什么 shared_ptr 实现是两个指针的大小。

标签: c++ pointers c++14 shared-ptr weak-ptr


【解决方案1】:

提案 P0033 在 2015 年 10 月的会议上被 C++17 接受,该提案将 weak_from_this 添加到派生自 std::enable_shared_from_this 的类中。

【讨论】:

    【解决方案2】:

    有没有办法让我的班级只提供weak_ptr 而无需先创建shared_ptr?

    不在 C++14 中; enable_shared_from_this 支持的唯一操作是创建 shared_ptr。现在,enable_shared_from_this 应该有足够的信息直接构造一个weak_ptr。但是您不能从外部执行此操作,因为该类不会向您公开其实现细节。

    C++17 支持通过weak_from_thisenable_shared_from_this 类中获取weak_ptr

    【讨论】:

    • 我接受这个,因为就 C++14 而言,它是正确的答案。我曾希望某些大师会发布一个巧妙的解决方法,但看起来不太可能。
    • enable_shared_from_this类创建weak_ptr的等效函数是方法weak_from_this()
    • @Fibbles 您可以自己维护一个weak_ptr 成员并删除enable_shared_from_this
    • @curiousguy:如果您愿意编写自己的 shared/weak_ptr 类型,您只能这样做。看,shared_ptr 在其构造函数中有特殊代码,这样,当您将对象与指针关联时,它会检测该对象是否派生自 enable_shared_from_this。如果是,它可以将指向共享状态的指针传递给该基类。绕过enable_shared_from_this 将要求您实现自己的shared_ptr 类型,或者在将对象与shared_ptr 关联时手动初始化基类。
    • @curiousguy:但这不是“实施细节”;它是您类型的接口 的一部分,这一点可以通过继承的shared/weak_from_this 函数成为 接口的一部分来证明。是的,强迫每个人在实际上不需要时使用工厂是愚蠢的。
    【解决方案3】:

    实现起来几乎是微不足道的,不值得放入库中......

    #include <memory>
    
    template<class T> std::weak_ptr<T> weak_from_this(T*p) {
      return { p->shared_from_this() };
    }
    
    struct S : std::enable_shared_from_this<S>
    {
      auto foo() {
        return weak_from_this(this);
      }
    };
    
    
    int main()
    {
      auto ps = std::make_shared<S>();
      auto wps = ps->foo();
    }
    

    【讨论】:

    • 要在这里展示我的无知,但 RVO 是否阻止了 shared_ptr 的构建?否则,原子引用计数器仍会递增。
    • 我认为 OP 知道现有的转换;这个想法是为了避免中间引用计数增加。
    • @KerrekSB 我明白了。不,在 c++17 之前没有办法避免颠簸,因为标准甚至没有表明在此之前存在这样的 weak_ptr。
    • @RichardHodges:C++17 也救不了你。 shared_from_this 的返回值为shared_ptr。因此,为了得到返回值,它必须经过隐式转换,这是合法的。但这意味着,在某一时刻,shared_ptrweak_ptr 都存在。 C++17 的保证省略规则仅在您初始化相同类型时保证省略。
    • @bolas 我指的是 c++17 中新的 weak_from_this 成员函数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-25
    • 1970-01-01
    • 2023-02-23
    • 1970-01-01
    • 1970-01-01
    • 2021-03-02
    相关资源
    最近更新 更多