Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do non-intrusive smart pointers behave with respect to inheritance and multiple inheritance?

I am using C++. C++0x using Visual Studio 2010 to be correct.

Suppose I have a class Z. To make it safer in my application to work with pointers to this class, I can consistently use smart pointers (shared pointer, weak pointer).

Now this class Z inherits from a class X. Some parts of my application will work with pointers to class X, others will work with pointers to class Z.

  • Can I still use smart pointers?
  • Do shared pointers still work if I have some that refer to X and others that refer to Z? Is it guaranteed that the destruction of the last shared pointer to the instance (regardless of whether it is std::shared_ptr<X> or std::shared_ptr<Z>) deletes the instance? Am I sure that if I delete std::shared_ptr<X>, that the instance is kept as long as there is another std::shared_ptr<Y>?

Now suppose that I use multiple inheritance, where Z inherits from classes X and Y. Some parts of my application will work with std::shared_ptr<X>, others with std::shared_ptr<Y> and others with std::shared_ptr<Z>.

  • Can I still use shared pointers this way?
  • Is it still guaranteed that only the last smart pointer (regardless of whether it points to X, Y or Z) deletes the instance?

By the way, how can I safely cast one smart pointer to another, e.g. cast std::shared_ptr<Z> to std::shared_ptr<X>? Does this work? Is this allowed?

Notice that I explicitly refer to non-intrusive pointers (as the new std::shared_ptr and std::weak_ptr in C++0x). When using intrusive pointers (like in Boost), it probably works since the instance itself is responsible for keeping the counter.

like image 800
Patrick Avatar asked Oct 13 '10 15:10

Patrick


1 Answers

Yes this is supported by the standard, §20.9.11.2.10 [util.smartptr.shared.cast].

The utils you need are:

  • std::static_pointer_cast<>()
  • std::dynamic_pointer_cast<>()

They have the same semantics as their C++03 counter parts static_cast<>() and dynamic_cast<>(). The one difference being that they only work on std::shared_ptrs. And just to verbose, they do what you expect and correctly share the reference count between the original and newly cast shared_ptrs.

struct X { virtual ~X(){} };
struct Y : public X {};
struct Z : public X {};

int main()
{
   {
      //C++03
      X* x = new Z;
      Z* z = dynamic_cast<Z*>(x);
      assert(z);
      x = new Y;
      z = dynamic_cast<Z*>(x);
      assert(!z);
      z = static_cast<Z*>(x);
      assert(z); //EVIL!!!
   }

   {
      //C++0x
      std::shared_ptr<X> x{new Z};
      std::shared_ptr<Z> z{std::dynamic_pointer_cast<Z>(x)};
      assert(z);
      x = std::make_shared<Y>();
      z = std::dynamic_pointer_cast<Z>(x);
      assert(!z);
      z = std::static_pointer_cast<Z>(x);
      assert(z); //EVIL!!!

      // reference counts work as expected.
      std::shared_ptr<Y> y{std::static_pointer_cast<Y>(x)};
      assert(y);

      std::weak_ptr<Y> w{y};
      assert(w.expired());

      y.reset();
      assert(w.expired());

      x.reset();
      assert(!w.expired());      
   }
   {
      //s'more nice shared_ptr features
      auto z = std::make_shared<X>();
      std::shared_ptr<X> x{z};
      assert( z == x );
      x = z; //assignment also works.
   }
}
like image 119
deft_code Avatar answered Nov 15 '22 12:11

deft_code