从指向的对象中重新分配 shared_ptr

re-assigning shared_ptr from within object pointed to

考虑以下代码

struct base {
  virtual void bar(std::shared_ptr<base>&) = 0;
};

struct foo1 : base { /* ... */ };

struct foo2 : base {
  void bar(std::shared_ptr<base>&obj)
  {
    // ...
    if(some_condition)
      obj = std::make_shared<foo1>();  // re-assign obj
    // ...
  }
};

std::shared_ptr<base> x = std::make_shared<foo2>();
x->bar(x);                             // last line

此代码是否调用 UB? (注意:从最后一行调用时,obj 的重新分配将破坏 call foo2::barthis 对象)。如果之后bar的数据都没有访问,代码还可以吗?

另一种编码方式是

struct base {
  virtual std::shared_ptr<base> bar(std::shared_ptr<base>) = 0;
};
struct foo1 : base { /* ... */ };

struct foo2 : base {
  std::shared_ptr<base> bar(std::shared_ptr<base> obj)
  {
    // ...
    if(some_condition)
      obj = std::make_shared<foo1>();
    // ...
    return obj;
  }
};

std::shared_ptr<base> x = std::make_shared<foo2>();
x=x->bar(x);

在任何情况下哪个应该是安全的,不是吗?此代码中的额外复制是性能问题吗?


编辑。 Chris 回答后,我查看了 shared_from_this,它允许以下替代实现

struct base : std::enable_shared_from_this<base> {
  virtual std::shared_ptr<base> bar() = 0;
};
struct foo1 : base { /* ... */ };

struct foo2 : base {
  std::shared_ptr<base> bar()
  {
    auto obj = shared_from_this();
    if(some_condition)
      obj = std::make_shared<foo1>();
    // ...
    return obj;
  }
};

std::shared_ptr<base> x = std::make_shared<foo2>();
x=x->bar();

正如这个 link 所指出的,它是安全的而不是 UB 到 delete this 的方法,即使它有点混乱。

Is it safe to `delete this`?

我认为额外的复制不是问题。可以肯定的是,您必须进行概要分析。缺少分析信息,如果是我,我更喜欢第二种方法而不是第一种方法 b/c 程序员更容易理解,但这主要是个人意见。

如果你想让对象更简单地操作指向自身的共享指针等,你可能还想查看 C++11 中的 shared_from_this 模板。