سؤال

I am new to C++ style casts, and need help in understanding how the code below works (this is some dummy code I wrote to understand things).

#include <iostream>
#include <memory>

class A { 
public:
    A() : a(1) {
        std::cout << "Creating A\n";
    }   
    ~A() {
        std::cout << "Destroying A\n";
    }   
    int a;
};

class B : public A { 
public:
    B() : b(2) {
        std::cout << "Creating B\n";
    }   
    ~B() {
        std::cout << "Destroying B\n";
    }   
    int b;
};

int main() {
    std::shared_ptr<B> objectB(new B());
    {   
    std::shared_ptr<A> (static_cast<A*>(objectB.get()));
    std::cout << "End of inner scope\n";
    }   
    std::cout << "End of outer scope\n";
}

It prints

Creating A
Creating B
Destroying A
End of inner scope
End of outer scope
Destroying B
Destroying A

My understanding:

Creating A         -> B's ctor calls base class ctor
Creating B         -> B's ctor
Destroying A       -> ???
End of inner scope
End of outer scope
Destroying B       -> B's dtor
Destroying A       -> B's dtor calls base dtor

Why do I get the first Destroying A and what exactly is happening here?! How can A be destroyed twice?

هل كانت مفيدة؟

المحلول

If you make sure the output is flushed (by using std::endl, for example), you get

Creating A

Creating B

Destroying A

End of inner scope

End of outer scope

Destroying B

Destroying A

the reason for the double delete of A is that you are constructing a shared_ptr from a raw pointer here:

std::shared_ptr<A> (static_cast<A*>(objectB.get()));

This shared_ptr is completely independent of the first one, and has its own reference count. So when the scope ends, it attempts to delete the A pointer it holds. If you had done this instead:

std::shared_ptr<A>{objectB};

then you wouldn't have encountered the problem. Note there is no need for a static_cast here.

Note that A should have a virtual destructor. shared_ptr has a clever destruction mechanism which means that this is not crucial in this example, but in general, if you are going to delete objects polymorphically, the base class must have a virtual destructor.

نصائح أخرى

Just in case it wasn't clear from juanchopanza's answer, this line

std::shared_ptr<A> (static_cast<A*>(objectB.get()));

is incorrect and leads to undefined behavior.

The shared pointer you construct here just takes the pointer you give it. It has no idea that the object pointed to by that pointer is already owned by another smart pointer. Furthermore it doesn't know that the pointer points to a sub-object and that the pointer will need to be cast back to B* in order to be deleted, because the static_cast hides that information from it.

To cast smart pointers you need a cast that understands and is integrated with smart pointers. C++ has std::static_pointer_cast and std::dynamic_pointer_cast for this purpose.

int main() {
  std::shared_ptr<B> objectB(new B());
  {   
    std::shared_ptr<A> x = std::static_pointer_cast<A>(objectB);
    std::cout << "End of inner scope\n";
  }   
  std::cout << "End of outer scope\n";
}

With this code the output of your program shows correct behavior:

make A
make B
End of inner scope
End of outer scope
~B
~A

Of course in this particular case you don't need an explicit cast, because std::shared_ptr can figure out legal up-casts and do them implicitly. You need std::static_pointer_cast to do down-casts though:

int main() {
  std::shared_ptr<A> objectA(new B());
  {   
    std::shared_ptr<B> x = std::static_pointer_cast<B>(objectA);
    std::cout << "End of inner scope\n";
  }   
  std::cout << "End of outer scope\n";
}

Thanks, one last thing, if I don't need objectB, is it safe to do std::shared_ptr<A> objectA(new B());?

Yes, this does happen to be safe. The shared_ptr<A> constructor receives a B* and knows enough to store the fact that when deletion occurs it will need to convert the A* that it holds to B*. This ensures that the object gets deleted correctly.

However, if you really want your types A and B to behave polymorphically then you should probably just make them polymorphic types by adding virtual destructors, and then you don't have to worry about how smart std::shared_ptr is about this.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top