1

I love Boost's smart_ptr features and the ability to convert to and from a shared_ptr and weak_ptr, but since the reference count is not contained in the pointed class itself, the following code does not work (and it shouldn't).

A *a = new A;
shared_ptr<A> aPtr1(a);

{
    shared_ptr<A> aPtr2(a);

    // The reference counts of aPtr1 and aPtr2 are both 1.
}   // At this point, `a` is destructed by aPtr2.

aPtr1->foo();   // And... SIGTERM

I believe the JUCE framework has this functionality. [ReferenceCountedObject and ReferenceCountedObjectPtr] However, I'd rather use Boost for my application. Is it possible to allow Boost smart_ptrs to look for the reference count in the pointed class rather than the private boost::detail::shared_count instance?

Vortico
  • 2,610
  • 2
  • 32
  • 49
  • It seems that what I want is a counted body/attached smart pointer, from [this](http://www.boost.org/community/counted_body.html) article. – Vortico Nov 11 '12 at 20:10
  • Just don't mix smart and plain pointers in your program, choose one approach and stick to it. – chill Nov 11 '12 at 20:46
  • I use `shared_ptr`s and `weak_ptr`s in all cases, but constructors only have access to the raw pointer `this`. Currently I am unable to initialize fields in the constructor so that there is a bidirectional relationship between, say `a` and `a->b`. – Vortico Nov 11 '12 at 21:04

3 Answers3

2

boost::intrusive_ptr likely fits your requirements.

To note however, with shared_ptr, you should construct them as follows:

shared_ptr<A> aPtr1 = boost::make_shared<A>();
Bill Lynch
  • 80,138
  • 16
  • 128
  • 173
2

Simple solution:

A *a = new A;
shared_ptr<A> aPtr1(a);
{
    // construct new shared pointer from old one.
    shared_ptr<A> aPtr2(aPtr1);
}
aPtr1->foo();

If you want something more complicated, see http://www.boost.org/libs/smart_ptr/enable_shared_from_this.html :

The header <boost/enable_shared_from_this.hpp> defines the class template enable_shared_from_this. It is used as a base class that allows a shared_ptr to the current object to be obtained from within a member function.


Edit: I should have mentioned that enable_shared_from_this has some unfortunate issues with derivation. However, the following works with c++11; I didn't try it with boost but I suppose it should work there, too. I think it's a bit of a hack; using raw pointers when you use shared_ptr's is bound to end in tears:

struct X : std::enable_shared_from_this {
  /* stuff */
};

struct Y : X {
  /* stuff */
  void yonly() {};
};

int main() {
  Y* y = new Y;
  shared_ptr<Y> sy(y);
  sy->yonly();
  {
    auto sy2 = std::shared_ptr<Y>(y->shared_from_this(), y);
    sy2->yonly();
  } 
  std::cout << "Block ended" << std::endl;
  return 0;
} 
rici
  • 234,347
  • 28
  • 237
  • 341
  • I should have mentioned that my example was too simple (as with many SO questions). I don't actually have access to the `aPtr1` variable when `aPtr2` is created. Unfortunately, `enable_shared_from_this` won't work for my purposes either since it does not truly support polymorphism. Derived classes cannot inherit from `enable_shared_from_this` if base classes do as well. – Vortico Nov 11 '12 at 20:15
  • @Vortico, yes, when asking questions it is always good to explain what solutions you have tried and rejected. Anyway, I added a bit of a hack for a possible way to use enable_shared_from_this somewhat polymorphically. – rici Nov 11 '12 at 21:14
  • That's good advice. I'll do that in the future.\n Thanks for the example in the edit! I think I will use that method if it will work on Boost shared_ptr. – Vortico Nov 12 '12 at 04:28
0

This is not exception safe:

// call this as in shared_ptr<T> foo = create_intrusive_shared( new T(blah) );
// This takes ownership of the pointer you pass it.
template<typename T>
std::shared_ptr<T> create_intrusive_shared( T* t )
{
  auto retval = std::shared_ptr<T>( t, []( T* cleanup )
  {
    if (cleanup)
      cleanup->RemoveRef();
  });
  return retval;
}

// Call this if you have an existing instance of T, whose ownership is being
// maintained elsewhere.  Do not call it with new T() as an argument, unless
// new instances of T are created with a 0 ref count
template<typename T>
std::shared_ptr<T> make_intrusive_shared( T* t )
{
  if (t)
    t->AddRef();
  auto retval = create_intrusive_shared(t);
  return retval;
}

Making them exception safe takes a bit more work. You will want to reimplement make_shared, but tag the resulting shared_ptr with a cleanup function.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524