views:

114

answers:

3

I was browsing for an alternative to using so many shared_ptrs, and found an excellent reply in a comment section:

Do you really need shared ownership? If you stop and think for a few minutes, I'm sure you can pinpoint one owner of the object, and a number of users of it, that will only ever use it during the owner's lifetime. So simply make it a local/member object of the owners, and pass references to those who need to use it.

I would love to do this, but the problem becomes that the definition of the owning object now needs the owned object to be fully defined first. For example, say I have the following in FooManager.h:

class Foo; 
class FooManager
{
    shared_ptr<Foo> foo;
    shared_ptr<Foo> getFoo() { return foo; }
};

Now, taking the advice above, FooManager.h becomes:

#include "Foo.h"
class FooManager
{
    Foo foo;
    Foo& getFoo() { return foo; }
};

I have two issues with this. First, FooManager.h is no longer lightweight. Every cpp file that includes it now needs to compile Foo.h as well. Second, I no longer get to choose when foo is initialized. It must be initialized simultaneously with FooManager. How do I get around these issues?

+3  A: 

If you don't need the shared ownership semantics of shared_ptr, consider using a different smart pointer container.

With the example you give (one object having ownership and no transferral of ownership), boost::scoped_ptr would be a good choice.

If you don't want to use Boost, scoped_ptr is very easy to implement--it's well described in the Boost documentation and its implementation (in boost/shared_ptr.hpp) is straightforward.

James McNellis
Just keep in mind that that makes `FooManager` noncopyable.
Billy ONeal
Yes, that the job for `boost:scoped_ptr`. Set it whenever via `void reset(T * p = 0); // never throws`.
Nikolai N Fetissov
@BillyONeal: That's true: it suppresses the implicit copy constructor and assignment operator, but it doesn't prevent you from implementing a user-defined copy constructor and assignment operator. If you don't need shared ownership, though, this likely won't be a big problem.
James McNellis
+3  A: 

You can use a shared_ptr ( or any smart pointer, or even a dumb pointer) but not have shared ownership.

E.g.

class Foo; 
class FooManager
{
  private:
    shared_ptr<Foo> foo;
  public:
    Foo& getFoo() { return *foo; }
};

(This is just a sketch - you still need a setFoo(), and perhaps getFoo() should return a Foo *. But the point is that you're back to being lightweight, and you can control when foo is created.)

brainjam
I'd add that in this case an auto_ptr has the best combination of flexibility and simplicity, since he doesn't need reference counting capabilities anymore.
Fabio Ceconello
Isn't auto_ptr well understood to be tricky to use correctly, due to "somewhat strange" copy semantics?
dash-tom-bang
+2  A: 

Use the pimpl idiom, and stop inlining so much.

FooManager.h:

class Foo;

class FooManager
{
   struct Impl;
   Impl *m_impl;
public:
   Foo& getFoo();

   FooManager();
   ~FooManager();
};

FooManager.cpp

#include "Foo.h"
#include "FooManager.h"

struct FooManager::Impl
{
   Foo* m_foo;
   int m_someothermember;
   FooManager::Impl() : m_foo(NULL), m_someothermember(0) {}
};

FooManager::FooManager() : m_impl(new FooManager::Impl())
{}

Foo& FooManager::getFoo()
{
   // Lazy initialization
   if( !m_impl->m_foo ) {
      m_impl->m_foo = new Foo;
   }
   return *m_impl->m_foo;
 }

 FooManager::~FooManager()
 {
    delete m_impl->m_foo;
    delete m_impl;
 }

Since you're already using boost I would recommend you use scoped_ptr to implement this in your real code instead of manually managing the memory as I've done in this example. The important thing to keep in mind is forward declaring works just as well for references as it does for pointers (which is part of the reason it worked for shared_ptr).

Logan Capaldo
Is the pimpl idiom critical to the solution? What is the relevant advantage over `scoped_ptr<Foo> m_foo` (or even just Foo* m_foo) in the header and `Foo return *m_foo }` in the cpp file?
Kyle
No it is not. The pimpl idiom was targeted at the "reduce header bloat" aspect, you need not go this far.
Logan Capaldo