Search code examples
c++apipass-by-referenceshared-ptrreference-counting

To get reference counting, do I have to clutter my APIs with shared_ptr?


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.


Solution

  • 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.