繁体   English   中英

C ++ shared_ptr :: operator *是否危险?

[英]Is C++ shared_ptr::operator* dangerous?

  shared_ptr<int> ptr = make_shared<int>(10);
  int& val = *ptr;
  ptr = make_shared<int>(20);
  // val now points to freed memory

在上面的代码中,我可以读取和写入val,该val指向释放的内存。 如果我们在shared_ptr中使用.get(),则会出现相同的问题。 因此,即使我们诉诸使用智能指针,也可以朝自己的脚射击。

显然没有人会像上面那样编码。 实现这一目标的一种方法是,如果我们有这样的东西-

class Foo {
public:
   int& getVal() { return *p; }
private:
   shared_ptr<int> p;
};

有人可以调用getVal(),之后上述类的其他一些成员可以选择用其他值覆盖p。 如果上面的getVal()返回shared_ptr而不是引用,则不会出现此问题。 有些人可能会争辩说,由于我们需要在shared_ptr控制块中增加计数器,因此返回shared_ptr比返回引用更昂贵。

因此,指南是否应该不像上面那样返回对shared_ptr的引用?

这里不是危险,不是operator* ,而是存储引用。

存储对任何事物的引用意味着您对所引用事物的生命周期负有个人责任。

int getVal() { return *p; }

是最安全,最简单的解决方案。 C ++崇尚价值观。

是否有一个更复杂的对象,其复制成本很高? 然后请多加注意。

gsl::span<int const> getVals() { return {p->data(), p->size()}; }

在这里,尽管没有引用或指针类型,但在概念上我们还是有一些引用。

它的消费者必须知道getVals返回的生命周期规则。 如果他们想保留它,则负责复制数据。

仅在极端情况下,您才应考虑:

std::shared_ptr<gsl::span<int const>> getVals() {
  return p?make_shared_span<int const>( p, {p->data(), p->size()} ):nullptr;
}

其中make_shared_span将共享的跨距视图创建到共享的ptr中。

共享对可变数据的引用特别令人恐惧。 您永远不会知道数据是否会在您的脚下改变,这会使使用它的任何代码的状态都与也引用它的所有代码的状态纠缠在一起。


C ++代码库解决“共享引用”和生存期问题的一种方法是使用不可变数据。

实际上是不可变shared_ptr<gsl::span<int const>const>是一个比shared_ptr<gsl::span<int>>更明智的选择。

您会在设计中看到这种情况,例如此处或使用不可变的libgit store支持文档。


通过将共享的指针传递给所有数据来“解决”这一问题,将导致对象的生存期大大超出其应有的寿命,因为人们存储了一些指向它们的共享指针而不再打算再次使用它。

注意一生,您将获得出色的RAII结果。

在某些情况下,共享一个共享指针是有意义的,也就是说,当您真正想要共享所有权时 ,而不是在“我不想考虑生命周期”时。 共享所有权是一种更复杂的生命周期,使用它是因为在某些情况下您不必考虑生命周期导致意大利面条的生命周期。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM