为了获得引用计数,我是否必须使用shared_ptr来扰乱我的API?

发布于 2024-08-20 11:35:33 字数 1544 浏览 5 评论 0原文

我最近遇到了以下内存错误,在这里很容易发现,但在更复杂的代码中可能更难检测到:

class Foo : public IFoo {
  const Bar& bar_;
public:
  Foo(const Bar& bar) : bar_(bar) {
  }
  void test() {
    // access bar_ here
  }
};

int baz() {
  IFoo* foo = NULL;
  if(whatever) {
    Bar bar;
    foo = new Foo(bar);
  }
  else {
    // create other foo's
  }
  foo->test(); // segmentation fault
}

该错误是 Bar 立即超出范围,被销毁,然后在 foo->test() 中使用。一种解决方案是使用 Bar* bar = new Bar() 在堆上创建 Bar。但是,我不喜欢这样做,因为我必须将 Bar* bar 指针保留在顶层,以便我可以在最后删除它,即使 Bar 是特定于该特定代码块 if(whatever){} 的内容。

另一个解决方案是 boost::shared_ptr,但我不能只这样写:

  if(whatever) {
    boost::shared_ptr<Bar> bar(new Bar());
    foo = new Foo(*bar);
  }

因为 shared_ptr 也会立即超出范围,从而破坏所包含的对象。

简而言之,为了摆脱这个问题,我必须在任何地方使用 shared_ptr,在 Foo 中作为成员变量,在 >Foo 的构造函数等。为了消除这些问题,我的所有 API 等都必须使用 shared_ptr,这有点难看。但是,这样做是正确的吗?到目前为止,我有时会使用它来创建引用计数对象,但我的 API 中没有 shared_ptr。您如何处理这个问题:一旦您使用了shared_ptr,您就必须到处使用它?

(另外,如果您使用这些引用计数指针,您必须开始担心您是否真的想要 shared_ptr 还是 weak_ptr 等)

并且,我将使用什么作为相当于 Foo(const Bar& bar)Foo(const shared_ptrbar)?

当然,另一种选择是使用 pimpl 和您自己的计数器在 Bar 和其他对象中添加引用计数,但一般来说这太乏味了。

I recently had the following memory bug, which is easy to spot here, but can be harder to detect in more complex code:

class Foo : public IFoo {
  const Bar& bar_;
public:
  Foo(const Bar& bar) : bar_(bar) {
  }
  void test() {
    // access bar_ here
  }
};

int baz() {
  IFoo* foo = NULL;
  if(whatever) {
    Bar bar;
    foo = new Foo(bar);
  }
  else {
    // create other foo's
  }
  foo->test(); // segmentation fault
}

The bug is that Bar immediately goes out of scope, is destroyed and then used in foo->test(). One solution is to create Bar on the heap, using Bar* bar = new Bar(). However, I don't like to do this because I'd have to keep the Bar* bar pointer at top-level so I can access and delete it at the end, even though Bar is something that is specific to that particular code block if(whatever){}.

Another solution is boost::shared_ptr<Bar>, but I cannot just write this:

  if(whatever) {
    boost::shared_ptr<Bar> bar(new Bar());
    foo = new Foo(*bar);
  }

since the shared_ptr goes out of scope immediately, too, destroying the contained object.

So in short, to get rid of this problem, I'd have to use shared_ptr everywhere, in Foo as member variable, in Foo's constructor, etc. To eliminate these problems in general, all my APIs etc. have to use shared_ptr, which is kind of ugly. But, is it the right thing to do? So far, I have used it sometimes to create reference-counted objects, but I have kept my APIs clean of shared_ptr. How do you deal with this problem that, once you use shared_ptr you have to use it everywhere?

(Also, if you use these reference-counted pointers, you have to start worrying about if you really want shared_ptr or rather weak_ptr etc.)

And, what would I use as an equivalent to Foo(const Bar& bar)? Foo(const shared_ptr<const Bar> bar)?

Another option, of course, is to add reference counting inside the Bar and other objects yourself, using pimpl and your own counters, but that gets too tedious as a general rule.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

心清如水 2024-08-27 11:35:34

我实际上会提出另一种解决方案...

我不太喜欢引用计数,不仅仅是混乱,而且还因为当多个对象拥有同一项目的句柄时很难推理。

class IBar
{
public:
  virtual ~IBar() {}
  virtual IBar* clone() const;
};

class Foo: public IFoo
{
public:
  Foo(const IBar& ibar): m_bar(ibar.clone()) {}
  Foo(const Foo& rhs): m_bar(rhs.m_bar->clone()) {}
  Foo& operator=(const Foo& rhs) { Foo tmp(rhs); this->swap(tmp); return *this; }
  virtual ~Foo() { delete m_bar; }

  void swap(Foo& rhs) { std::swap(m_bar, rhs.m_bar); }

private:
  IBar* m_bar;
};

现在你的代码可以工作了,因为我做了一个副本:)

当然,如果 Bar 不是多态的,那么它会容易得多。

那么,即使它没有使用参考,您确定您确实需要一个吗?


现在让我们偏离正轨,因为你做错了很多事情:

  • foo = new Foo(bar) 不太漂亮,你如何保证内存被删除,以防万一发生异常或在 if 混乱中引入一些 return ?您应该转向为您管理内存的对象 auto_ptr 或来自 C++0x 或 Boost 的新 unique_ptr
  • 为什么 Foo 不获取所有权,而不是使用引用计数? Foo(std::auto_ptrbar) 也可以。

在不需要的地方回避引用计数。每次您分享某些内容时,您实际上会在代码中引入难以跟踪的错误来源(以及潜在的副作用)。这就是 Haskell 和函数式家族越来越受欢迎的原因:)

I would actually propose another solution...

I don't really like reference counting that much, not just the clutter, but also because it's harder to reason when multiple objects have a handle to the same item.

class IBar
{
public:
  virtual ~IBar() {}
  virtual IBar* clone() const;
};

class Foo: public IFoo
{
public:
  Foo(const IBar& ibar): m_bar(ibar.clone()) {}
  Foo(const Foo& rhs): m_bar(rhs.m_bar->clone()) {}
  Foo& operator=(const Foo& rhs) { Foo tmp(rhs); this->swap(tmp); return *this; }
  virtual ~Foo() { delete m_bar; }

  void swap(Foo& rhs) { std::swap(m_bar, rhs.m_bar); }

private:
  IBar* m_bar;
};

And now your code works, because I made a copy :)

Of course, if Bar was NOT polymorphic, then it would be much easier.

So, even though it's not using a reference, are you sure you actually need one ?


Let's get off track now, because there are a number of things you do wrong:

  • foo = new Foo(bar) ain't pretty, how are you going to guarantee that the memory is deleted in case an exception occurs or somewhat introduces some return in the if clutter ? You should move toward an object that manages memory for you auto_ptr or the new unique_ptr from C++0x or Boost.
  • instead of using reference counting, why does not Foo takes ownership ? Foo(std::auto_ptr<Bar> bar) would work too.

Shy from reference counting where it's not needed. Every time you share something you actually introduce a source of hard to track bugs in your code (and potentially side effects). That's why Haskell and the functional family are gaining popularity :)

偷得浮生 2024-08-27 11:35:34

另外,如果你使用这些引用计数指针,你必须开始担心你是否真的想要shared_ptr还是weak_ptr等

如果你遵循你的设计并且你理解什么是weak_ptr(提示:它不是参考)以及它的用途(提示:它不是为了“打破循环”)。

如果您确实开始担心,那是因为您从未进行过设计,或者您不了解 shared_ptr 设计。

Also, if you use these reference-counted pointers, you have to start worrying about if you really want shared_ptr or rather weak_ptr etc

There is no worrying if you follow your design and you understand what is weak_ptr (hint: it isn't a reference) and what it is for (hint: it isn't for "breaking cycles").

If you do start worrying, it's because you never had a design, or you don't understand shared_ptr design.

淡淡の花香 2024-08-27 11:35:34

您可以简单地将 bar 对象移动到更高的范围,以防止过早删除它。

int baz() {
  Bar bar;
  IFoo* foo = NULL;
  if(whatever) {
    foo = new Foo(bar);
  }
  else {
    // create other foo's
  }
  foo->test(); // no more segmentation fault
}

You could simply move your bar object at a higher scope in order to prevent its deletion too soon.

int baz() {
  Bar bar;
  IFoo* foo = NULL;
  if(whatever) {
    foo = new Foo(bar);
  }
  else {
    // create other foo's
  }
  foo->test(); // no more segmentation fault
}
扭转时空 2024-08-27 11:35:33

事实上,我确实在任何地方都使用shared_ptr...有几种方法可以让它看起来不那么混乱。我使用的一个约定是为每个定义的类使用 typedef:

class AClass {
public:
    typedef boost::shared_ptr<AClass> Ptr;
    typedef boost::weak_ptr<AClass> Ref;
    //...
};

使代码更具可读性:)

至于您的具体问题,请记住您可以通过指针传递 bar - 但您必须记住在堆上分配它。

Actually I do use shared_ptr everywhere... There are several ways to make it look less cluttered. One convention I use is typedefs for each defined class:

class AClass {
public:
    typedef boost::shared_ptr<AClass> Ptr;
    typedef boost::weak_ptr<AClass> Ref;
    //...
};

Makes the code much more readable :)

As for your specific problem, remember that you can pass bar by pointer -- you'd have to remember to allocate it on the heap though.

会发光的星星闪亮亮i 2024-08-27 11:35:33

最好不要将 bar byref 传递到 Foo 中(换句话说,更改 Foos 构造函数),或者复制它,以便在 Foo 中保留一个副本。当然,在某些情况下这是不可能的。

关于使用 shared_ptr 覆盖代码,最简单的解决方案是将 shared_ptr 键入 BarPtr 或类似的。

It would be better to either not pass bar byref into Foo (in other words change Foos constructor) or copy it so you hold a duplicate in Foo. Of course in some cases this is not possible.

Regarding covering your code with shared_ptr<Bar>, the easiest solution is to typedef shared_ptr<Bar> to BarPtr or similar.

夜吻♂芭芘 2024-08-27 11:35:33

我知道您提供了代码的简化版本,在没有看到其余部分的情况下,我不知道我现在所说的是否可行。

由于 foo 是寿命最长的对象,因此如何在其中创建 Bar 实例,并让其他变量(代码中的 bar)引用到那个?

另一方面,如果 Bar 确实特定于 if 块的内部,则 test() 根本不需要它。在这种情况下,将指针保留在 Foo 内然后在 if 块末尾删除它是没有问题的。否则,也许它不是那么特定于该块,您必须重新考虑您的设计。

I understand that you provide a simplified version of your code, and without seeing the rest I don't know if what I am saying now is feasible.

Since foo is the longest-living object, how about creating the Bar instance inside it, and making other variables (bar in your code) references to that?

On the other hand, if Bar is really sepecific to the inside of the if block, test() should not need it at all. In this case, there is no problem in keeping a pointer inside Foo and then deleting it at the end of the if block. Otherwise, maybe it is not so specific to the block and you have to rethink your design a bit.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文