【问题标题】:C++: can C++11 move semantics avoid pointers in shared-ownership cases?C++:C++11 移动语义可以避免共享所有权情况下的指针吗?
【发布时间】:2016-01-14 13:01:11
【问题描述】:

我有一个Bar 类和一些Foo 类的实例,其中包含Bar 对象的列表。我希望对底层Bar 对象进行任何更改,以更改Foo 列表中的Bar(所以副本是不可能的)。

是否可以避免使用带有 C++11 移动语义的指针?或者这种共享所有权是指针必不可少的情况之一?

指针实现:

struct Bar
{
public:
    int someProperty;
};

class Foo
{
public:
    std::vector<std::shared_ptr<Bar>> barList;
};

int main()
{
    std::shared_ptr<Bar> pBar(new Bar);

    Foo fooA;
    Foo fooB;

    fooA.barList.push_back(pBar);
    fooB.barList.push_back(pBar);

    pBar->someProperty = 5;

    return 0;
}

例如,是否有一个单独的类拥有Bar 对象,然后Foo 对象具有对它们的引用列表?喜欢std::vector&lt;std::reference_wrapper(Bar)&gt; barList?还是在这种情况下会首选指针?

有人建议我在 C++ 中过度使用指针,应尽可能使用引用。

【问题讨论】:

  • 如果您拥有 shared 所有权,那么您应该使用 shared_ptr
  • 您不能真正拥有引用容器,无论如何也不能直接拥有。您也许可以使用例如std::reference_wrapperstd::ref?但是std::shared_ptr制作是为了共享所有权。
  • 它是原始的 owning 指针,应该避免。指针可能没问题(observer_ptr 可能更好)。
  • 旁注:请习惯auto pBar = std::make_shared&lt;Bar&gt;()

标签: c++ pointers c++11


【解决方案1】:

虽然您可以创建一个架构,其中 Bar 对象拥有单独的所有者,而 Foo 对象仅保存指针列表,但这样的架构与移动语义无关,只关心此所有权模型是否在您的域中有意义。

但是,如果您创建这样的模型,请使用原始指针,或者如果您担心混淆,请使用非常愚蠢的指针包装器。引用在列表中不起作用,reference_wrapper 不会给你买任何东西。

如果您认为Foo 对象实际上拥有Bar 对象的共享所有权是有意义的,那么请使用shared_ptr 的容器。

无论哪种方式,移动语义都与它无关。

【讨论】:

  • 所以我不可能拥有一些BarRegister 拥有的Bar 实例,而不必在Foo 中使用指向Bar 的指针? IE。在这种情况下不能使用引用吗?此外,如果有一个BarRegister 负责Bar 对象,那么使用shared_ptrunique_ptr 是否更有意义,如果您可以保证每个Foo 都会在BarRegister 之前被销毁?
  • 没有办法使用引用,这样做也没有意义。原始指针或简单的observing_pointer 包装器完美地传达了意图,除了包装器可以强制执行的不可空性之外。如果您有一个 BarRegister 拥有 Bars,那么 unique_ptr 是正确的选择。
【解决方案2】:

您目前的问题暴露了您对“foo/bar”示例的过度简化。即,它忽略了这个问题:

您是否打算让Foo拥有它包含的Bar 对象?

并非每个具有对象指针/引用的类都打算拥有该对象。在许多情况下,您可以构建代码,以便通过类之间的关系来维护所有权。例如,如果Foo 是由某个FooMaker 监督对象创建的,则该对象可能负责创建(并拥有)存储在这些Foo 对象中的Bar 对象。或者Bar 对象可能为其他系统所拥有。

如果每个Foo 实例永远不会比它的Bar 实例的另一个所有者寿命长,那么Foo 不需要拥有它们。这就是为什么谈论“foo”和“bar”没有帮助的原因。没有上下文,我们不能说这里应该是什么正确的关系。

如果您打算让Foo 直接控制其中存储的Bar 对象的生命周期,那么它应该使用智能指针。但是,如果您的意图是让Foo 不控制存储在其中的Bar 对象的生命周期,那么它不应该使用智能指针。 C++ 中表示这一点的惯用方式是with a naked pointer(只是因为人们说某些东西已经“过度使用”并不意味着你应该完全停止使用它)或参考。由于您需要将它们存储在vector 中,因此指针更合适(为清楚起见,可能用GSL's not_null 装饰)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-31
    • 2013-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多