Someone I worked with once said that shared_ptr was unsafe and would slice when casting from a derived class to a base class (i.e. upcasting). For example if there were 2 cl
That someone is wrong, object slicing doesn't apply to pointers. That the pointer usage is wrapped away in a shared_ptr
doesn't change that - it doesn't do any particular magic here, it initializes an internal pointer with the value passed to its constructor.
Simplified it could look e.g. like this for the purpose of this question:
template<class T> struct ptr {
T* t;
ptr(T* t) : t(t) {}
// ...
};
You lose the static type-information of B
, yes, but nothing changes regarding the object pointed to.
Pointers are PODs (just for the record: shared_ptr
s aren't).
The question quotes:
shared_ptr can be implicitly converted to shared_ptr whenever T* can be implicitly converted to U*.
This is about converting from one type to another, which is different from upcasting. There's no inheritance relationship between shared_ptr<A>
and shared_ptr<B>
, whether or not A
derives from B
or viceversa. This is the reason why the shared_ptr
object itself doesn't slice.
Consider a class hierarchy A, B with no virtual destructors.
std::shared_ptr<A> a(new B);
auto a = std::make_shared<B>();
will capture B's deallocator and then will call B's destructor when needed
std::shared_ptr<A> a((A*)(new B));
will do no such thing and will cause slicing problems in the pointed-to object.
For example, using unique_ptr
has different behaviour:
std::unique_ptr<A> a(new B);
std::unique_ptr<A> a((A*)(new B));
will both exhibit slicing problems, while
auto a = std::make_unique<B>();
won't.
Using a plain pointer doesn't help either:
A* a = new B{};
delete a;
is a recipe for disaster.
Example code available here.