shared_ptr 到基础接口的初始化和管理
Initialization and management of a shared_ptr to base interface
我有一些关于使用指向基数 class 的 shared_ptr 的问题。他们的答案相互影响,对于所有三个,我需要相同的代码片段以尽可能少的方式设置上下文,比如 this(所有问题都与 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
和 Foo
一起使用的多态层次结构的指针。 ConcreteBaz
实际上代表一个 物理实体 ,如代码中所述,Foo
可以取得所有权(它是一个 shared_ptr
,因为 Scene
和 Foo
拥有它,根据 。我在这里删除了 Scene
的详细信息,因为它是 OT)。
问题
1) 如何初始化held_object_
?我可以一次分配吗?
实际上,foo
在它(物理上)得到它之前并不拥有 cbaz
,因此构造函数应该将指针初始化为 nullptr。最初我想根据 this but I read 使用 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
。这个 会 因此成为 this 问题的副本,但接受的答案要么使 shared_ptr
未初始化,要么创建一个进一步的 unique_ptr
到......基本上目前还不清楚我将如何在这里应用它,而且我不认为我可以在构造函数中调用 reset
(?).
我应该明确地说
Foo::Foo()
: held_object_ {std::shared_ptr<Baz> {nullptr}}
{
}
不关心 make_shared
避免的双重分配?在这一点上,它不会与 : held_object_ {}
几乎完全相同(将 ctor 与默认 ctor 放在一边)吗?
编辑:我也可以使用单个分配来完成它,例如 make_shared
does?
2) 如何管理 held_object_
?
现在在调用 acquire
之后获得 cbaz
的所有权,我最终调用了方法
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
class'run
方法,但是这样定义使得use_count 到 3 时 Scene
和 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 */);
}
}
或者有更好的方法吗?
How do I initialize held_object_
如果您希望 held_object_
成为 nullptr
,那么您无需执行任何操作。 Foo
的编译器生成的默认构造函数将调用 held_object_
的默认构造函数,将其保留为空指针。
How do I manage held_object_
当您需要释放指针的所有权时,您只需调用 reset
即可,无需任何参数。这有效地 shared_ptr().swap(*this);
给你留下了一个空指针。
1) How do I initialize held_object_?
你可以这样做:
Foo::Foo()
: held_object_ {nullptr}
{
}
但这当然等同于 : held_object_ {}
,尽管您的意图稍微更加明确。另一种选择是
class Foo
{
// ...
private:
std::shared_ptr<Baz> held_object_ = nullptr;
};
2) How do I manage held_object_?
您使用 std::move
的第一个版本就很好。请注意,您仅以这种方式参与所有权,您没有获得独占所有权(这就是为什么我不会将方法命名为 acquire
)。
3) There are really only two owners
我不认为你能得到这个问题的真正答案,因为它既取决于你的具体情况,也取决于你的具体情况(即使考虑到这一点)在某种程度上是基于意见的。另外,除非您实际上是为了提高性能,否则不要担心使用次数。因为您担心一些微小的性能细节(shared_ptr
中的引用计数成本)而编写不同的代码是过早的优化,除非您有充分的理由相信(或证明)这将是一个问题。你在内循环中这样做吗?否则,将不会感觉到虚假的引用计数 up/down。
我有一些关于使用指向基数 class 的 shared_ptr 的问题。他们的答案相互影响,对于所有三个,我需要相同的代码片段以尽可能少的方式设置上下文,比如 this(所有问题都与 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
和 Foo
一起使用的多态层次结构的指针。 ConcreteBaz
实际上代表一个 物理实体 ,如代码中所述,Foo
可以取得所有权(它是一个 shared_ptr
,因为 Scene
和 Foo
拥有它,根据 Scene
的详细信息,因为它是 OT)。
问题
1) 如何初始化held_object_
?我可以一次分配吗?
实际上,foo
在它(物理上)得到它之前并不拥有 cbaz
,因此构造函数应该将指针初始化为 nullptr。最初我想根据 this but I read 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
。这个 会 因此成为 this 问题的副本,但接受的答案要么使 shared_ptr
未初始化,要么创建一个进一步的 unique_ptr
到......基本上目前还不清楚我将如何在这里应用它,而且我不认为我可以在构造函数中调用 reset
(?).
我应该明确地说
Foo::Foo()
: held_object_ {std::shared_ptr<Baz> {nullptr}}
{
}
不关心 make_shared
避免的双重分配?在这一点上,它不会与 : held_object_ {}
几乎完全相同(将 ctor 与默认 ctor 放在一边)吗?
编辑:我也可以使用单个分配来完成它,例如 make_shared
does?
2) 如何管理 held_object_
?
现在在调用 acquire
之后获得 cbaz
的所有权,我最终调用了方法
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
class'run
方法,但是这样定义使得use_count 到 3 时 Scene
和 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 */);
}
}
或者有更好的方法吗?
How do I initialize
held_object_
如果您希望 held_object_
成为 nullptr
,那么您无需执行任何操作。 Foo
的编译器生成的默认构造函数将调用 held_object_
的默认构造函数,将其保留为空指针。
How do I manage
held_object_
当您需要释放指针的所有权时,您只需调用 reset
即可,无需任何参数。这有效地 shared_ptr().swap(*this);
给你留下了一个空指针。
1) How do I initialize held_object_?
你可以这样做:
Foo::Foo()
: held_object_ {nullptr}
{
}
但这当然等同于 : held_object_ {}
,尽管您的意图稍微更加明确。另一种选择是
class Foo
{
// ...
private:
std::shared_ptr<Baz> held_object_ = nullptr;
};
2) How do I manage held_object_?
您使用 std::move
的第一个版本就很好。请注意,您仅以这种方式参与所有权,您没有获得独占所有权(这就是为什么我不会将方法命名为 acquire
)。
3) There are really only two owners
我不认为你能得到这个问题的真正答案,因为它既取决于你的具体情况,也取决于你的具体情况(即使考虑到这一点)在某种程度上是基于意见的。另外,除非您实际上是为了提高性能,否则不要担心使用次数。因为您担心一些微小的性能细节(shared_ptr
中的引用计数成本)而编写不同的代码是过早的优化,除非您有充分的理由相信(或证明)这将是一个问题。你在内循环中这样做吗?否则,将不会感觉到虚假的引用计数 up/down。