【问题标题】:I have a reference and want to call a function that takes boost::shared_ptr我有一个参考,想调用一个采用 boost::shared_ptr 的函数
【发布时间】:2012-01-25 15:16:23
【问题描述】:

我有一个对象的引用,并想调用一个函数来获取该对象的 boost::shared_ptr。如果我构建一个 boost::shared_ptr 以在我的 boost::shared_ptr 从堆栈中取消时进行调用,那么该对象也会被取消!这正是我运行此代码时发生的情况:

double f(boost::shared_ptr<Obj>& object)
{
  ...
}

double g(Obj& object)
{
  boost::shared_ptr<Obj> p(&object);
  double x = f(p);
  ...
}

有没有办法让它工作?如何在 g() 中创建一个 boost::shared 指针,让我的对象在最后保持活动状态?我想我必须将它连接到已经指向对象的其他共享指针的引用计数机制......但是如何?

即使我让它发挥作用,你认为这种做法是糟糕的设计吗?解决此类问题的最佳做法是什么?在我的代码中,我有对象和方法可以同时使用共享指针和引用,我不能只使用这些或那些......

【问题讨论】:

  • 为什么函数需要一个shared_ptr?
  • 函数 f() 是一个对象的方法,它包含 Obj 对象的属性的持久映射。这些属性存储在将共享指针链接到属性的映射中。需要共享指针来查找映射中的对象。我会说函数 f() 必须使用共享指针,因为它参与 Obj 对象的所有权。
  • @martino 为什么地图使用shared_ptr&lt;Obj&gt;,而不仅仅是Obj(在这种情况下,g 会使用Obj const&amp;,而不是shared_ptr&lt;Obj&gt;)。跨度>
  • 您是否考虑过制作对象的副本?即f(boost::shared_ptr&lt;Obj&gt;(new Obj(object))——或者你需要它来引用同一个对象吗?
  • @James 你所说的键是 Obj 的地图是什么意思?键应该是指向 Obj 的原始指针吗?

标签: c++ reference shared-ptr


【解决方案1】:

接受shared_ptr 的函数正在说明它的作用。它的意思是,“我想潜在地声明这个对象的共享所有权”。如果这不是真的,那么它是一个写得很糟糕的函数,根本不应该使用shared_ptr

通过非const 引用对象获取值的函数意味着该函数可以修改该对象,但不能声明所有权。如果您不拥有某物,您也无法将所有权转让给其他人。

现在,您可以执行这个使用空删除函数的技巧:

void EmptyDeleter(Obj *) {}

double g(Obj& object)
{
  boost::shared_ptr<Obj> p(&object, EmptyDeleter);
  double x = f(p);
  ...
}

但是,您现在f 撒谎。它不拥有object;它不能拥有objectobject 很有可能是一个堆栈对象,在 f 完成后的任何时候都可能消失。如果f 是一个类的成员,它可能会将shared_ptr 存储在一个成员变量中。此时,它将有一个shared_ptr 指向一个死对象。这正是shared_ptrs 旨在防止的那种事情。

正确的答案是f 不接受shared_ptr 的参数(如果它是可修改的,则使用非const 引用或非const 指针,如果它不可修改,则使用const&amp; ),或者让g 接受shared_ptr 的参数。

【讨论】:

  • +1 表示“正确答案”段落。在这里欺骗函数不是正确的解决方案。
【解决方案2】:

您可以创建一个shared_ptr,它实际上并没有释放该对象。像这样:

struct FictiveDisposer {
    template <class T> void operator ()(T) {}
};

Obj& object = /* ... */;
boost::shared_ptr<Obj> myPtr(&obj, FictiveDisposer ());

// you may use myPtr

但是你应该小心使用它。如果您确定您正在调用的函数不会尝试“保存”您的对象以供以后使用 - 没有问题。否则,您必须保证保存到对象的shared_ptr 的生命周期不会超过对象的实际生命周期。

简单来说:你得到了对象的引用。你没有创建它,你可能不会影响它的生命周期(shared_ptr 也不能)。因此,可能会出现对象不再存在的情况,但它仍然被shared_ptr 引用。必须避免这种情况。

【讨论】:

    【解决方案3】:

    您必须考虑 f() 的用途。据推测,如果它需要一个 shared_ptr,f 打算随着时间的推移保留这个指针的共享所有权,超过它的返回。为什么?当你传递一个 shared_ptr 时,f() 假设是什么?一旦你回答了这个问题,你将能够弄清楚如何编写 g()。

    如果由于某种原因 f() 不需要保留指针的共享所有权,那么如果您可以控制其接口,则可以将其重写为采用 Obj* 或 Obj& 而不是 shared_ptr。任何拥有 shared_ptr 的代码都可以通过将指针拉出 shared_ptr 或取消引用来调用 f。

    【讨论】:

      【解决方案4】:

      如果库界面采用 shared_ptr(但也有例外)。由于你的功能 呼叫期望承担责任,或至少部分承担 责任,你传递给它的对象,以及其余的代码 没有为此做好准备,您唯一安全的解决方案是克隆 对象,例如:

      double x = f( boost::shared_ptr<Obj>( new Obj( object ) ) );
      

      但你最好找出为什么 f 需要 shared_ptr,以及为什么 g 不能将其作为论据。

      【讨论】:

        【解决方案5】:

        如何在 g() 中创建一个 boost::shared 指针,让我的对象在最后保持活动状态?

        你可以给共享指针一个自定义析构函数,它什么都不做:

        boost::shared_ptr<Obj> p(&object, [](void*){});
        

        或者如果您的编译器不支持 lambda:

        void do_nothing(void*) {}
        boost::shared_ptr<Obj> p(&object, do_nothing);
        

        即使我让它发挥作用,你认为这种做法是糟糕的设计吗?

        是的:您失去了共享指针为您提供的生命周期管理,现在您有责任确保对象的寿命比所有共享指针都长。

        解决此类问题的最佳做法是什么?

        为您正在使用的所有对象确定所有权模型,并坚持下去。当您想要共享所有权时,共享指针可能很有用,但在其他情况下则不然。在您的情况下,f 想要共享所有权,而任何调用 g 似乎都想要独占所有权;考虑一下他们为什么要这样做是个好主意,以及您是否可以将其中一个更改为与另一个兼容。

        【讨论】:

          【解决方案6】:

          std::shared_ptr&lt;T&gt; 可能赋予多个实体对所引用对象的所有权。如果接口需要std::shared_ptr&lt;T&gt;,则有两种可能性:

          1. 有人无知地在接口中使用了std::shared_ptr&lt;T&gt;,该接口旨在接收T 对象或指向T 对象的引用或指针。如果是这种情况,作者应该接受教育(如果这不起作用,则从他目前的职责中解脱出来以追求新的职业)并更正界面。
          2. 由于现在所有使用std::shared_ptr&lt;T&gt; 的接口都使用它来可能授予对象共享所有权,很明显分配T 的堆栈不是此类接口的合适参数。唯一可能的参数是 T 对象,其生命周期通过 std::shared_ptr&lt;T&gt; 维护。

          我意识到这并不能回答最初的问题,但应该清楚的是,唯一可行的做法是:不要尝试将对象传递给采用 @ 的函数987654330@不能完全被这样的指针控制! (同样适用于boost 版本的共享指针)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-02-28
            • 1970-01-01
            • 1970-01-01
            • 2014-09-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多