3

我有一些与使用指向基类的 shared_ptr 有关的问题。他们的答案相互影响,对于所有三个,我需要相同的代码片段以尽可能少的方式设置上下文,就像这样(所有问题都Foo与它有关held_object_):

#include <memory>
#include <utility>

class Bar  // interface for Scene (see linked question)
{
public:
  virtual ~Bar() = 0;
  // more virtual methods to work with Scene
};

Bar::~Bar() = default;

class Baz : public Bar // interface for Foo
{
public:
  virtual ~Baz() = 0;
  // more virtual methods to work with Foo
};

Baz::~Baz() = default;

class ConcreteBaz : public Baz // actual thing I'm acquiring and releasing
{
// overrides and whatnot
};

class Foo
{
public:
  void acquire(const std::shared_ptr<Baz>& object) {};
  void release(/* actually takes a place to release into */) {};

private:
  std::shared_ptr<Baz> held_object_;
};

int main()
{  
  auto foo = std::make_unique<Foo>();
  auto p_foo = foo.get();

  while (/* condition */)
  {
    auto cbaz = std::make_shared<ConcreteBaz>();
    // Scene gets a copy here

    p_foo->acquire(cbaz);
    // do physical things to cbaz
    p_foo->release(/* actually takes a place to release into */);

    // do other physical things, then acquire cbaz again

    p_foo->acquire(cbaz);
    // do physical things to cbaz
    p_foo->release(/* actually takes a place to release into */);
  }
}

如您所见cbaz,它是一个指向多态层次结构的指针,它可以与Scene和一起使用FooConcreteBaz实际上代表一个物理实体Foo如代码中所述,可以拥有它(这是shared_ptr因为两者都Scene拥有Foo它,根据this。我删除了这里的细节,Scene因为它是 OT)。

问题

1)如何初始化held_object_?我可以一次分配吗?

实际上,在它(物理上)获得它之前foo并不拥有cbaz它,因此构造函数应该初始化指向 nullptr 的指针。最初我想make_shared按照这个使用,但我在这里读到它是值初始化的,这意味着如果我要这样做

Foo::Foo()
    : held_object_ {std::make_shared<Baz>()} // equivalent to std::make_shared<Baz>(nullptr)
{
}

编译器会抱怨error: invalid new-expression of abstract class type. 因此,这成为这个问题的副本,但接受的答案要么留下shared_ptr未初始化的内容,要么进一步创建unique_ptr......基本上不清楚我将如何在这里应用它,而且我认为我不能调用reset构造函数(?) .

我是否应该明确地说

Foo::Foo()
    : held_object_ {std::shared_ptr<Baz> {nullptr}}
{
}

不关心make_shared避免的双重分配?在这一点上,它不会几乎与 just 完全相同: held_object_ {}(将 ctor 与默认 ctor 放在一边)?编辑:我也可以像这样用单一分配make_shared 来做吗?

2) 我该如何管理held_object_

现在要cbaz在调用后获得所有权,acquire我最终会调用方法

void Foo::setHeldObject_(std::shared_ptr<Baz> object)
{
  this->held_object_ = std::move(object);
}

但是在release我将不得不销毁拥有shared_ptr(然后在下一个循环中再次设置它)并使我的Foo实例的状态与其物理状态 IRL 保持一致。这让我想到了使用std::shared_ptr::reset(甚至在阅读之前的相关答案之前),因为如果我调用setHeldObject()并设置cbaz其他方式,我会用 nullptr 替换指针。但是我无法将方法主体的正确语法计算为:

  • this->held_object_.reset(object);显然是错误的,因为我想管理指针,而不是指针(因此不会编译);
  • this->held_object_.reset(&object);看起来不对,并产生error: cannot convert ‘std::shared_ptr<Baz>*’ to ‘Baz*’ in initialization
  • this->held_object_.reset(object.get());也似乎是错误的,因为我认为我不会以user_count这种方式进行升级(尽管它确实可以编译)。

编辑:我相信应该可以用相同的方法调用来做到这一点,给出或不给出论点。那有可能reset吗?

3)真的只有两个所有者

我在这里写的main函数实际上是一个Process类的run方法,但是像这样定义它使得 use_count 到了 3Scene并且Foo有自己的shared_ptr. 从我make_shared开始,我就在一个循环中,而且逻辑上说应该只有两个所有者。这是一个用例,weak_ptr在哪里可以有意义地做类似的事情:

int main()
{  
  auto foo = std::make_unique<Foo>();
  auto p_foo = foo.get();

  while (/* condition */)
  {
    auto cbaz = std::make_shared<ConcreteBaz>();
    auto p_cbaz = std::weak_ptr<ConcreteBaz> {cbaz};
    // Scene gets a && via std::move here

    p_foo->acquire(p_cbaz.lock()); // this instantly gets converted to a shared_ptr EDIT: BUT IT DOESN'T INCREASE THE USE COUNT
    // do physical things to cbaz
    p_foo->release(/* actually takes a place to release into */);

    // do other physical things, then acquire cbaz again

    p_foo->acquire(p_cbaz.lock()); // this instantly gets converted to a shared_ptr and would be nullptr had I moved in the first acquire call EDIT: BUT IT DOESN'T INCREASE THE USE COUNT
    // do physical things to cbaz
    p_foo->release(/* actually takes a place to release into */);
  }
}

或者,还有更好的方法?

4

2 回答 2

2

如何初始化held_object_

如果你想held_object_成为,nullptr那么你不需要做任何事情。编译器生成的默认构造函数Foo将调用held_object_默认构造函数,将其保留为空指针。

我该如何管理held_object_

当您需要释放指针的所有权时,您只需调用reset而无需任何参数。这有效地shared_ptr().swap(*this);为您留下了一个空指针。

于 2018-12-04T14:50:17.663 回答
0

1)我如何初始化hold_object_?

你可以这样做:

Foo::Foo()
    : held_object_ {nullptr}
{
}

但这当然等同于: held_object_ {},尽管您的意图稍微更明确。另一种选择是

class Foo
{
  // ...
private:
  std::shared_ptr<Baz> held_object_ = nullptr;
};

2) 我如何管理hold_object_?

你的第一个版本std::move很好。请注意,您仅以这种方式参与所有权,您不会获得独占所有权(这就是我不命名该方法的原因acquire)。

3)真的只有两个所有者

我不认为你能得到一个真正的答案,因为它既取决于你的确切情况,而且(即使是这样)有点基于意见。另外,除非您实际上是出于性能考虑,否则不要担心使用次数。因为担心一些微小的性能细节(引用计数的成本shared_ptr)而编写不同的代码是过早的优化,除非您有充分的理由相信(或证明)这将是一个问题。您是否在内循环中执行此操作?如果没有,则不会感觉到虚假的向上/向下引用计数。

于 2018-12-04T14:51:47.937 回答