Consider this example constructing shared_ptr<T>
in various ways and returning:
#include <memory>
#include <iostream>
class Base
{
public:
virtual ~Base() {}
Base(int y) : y_(y) {}
int y_;
};
class Derived : public Base
{
public:
Derived(int y, int z) : Base(y), z_(z) {}
int z_;
};
std::shared_ptr<Base> A()
{
return std::shared_ptr<Base>(new Derived(1, 2));
}
std::shared_ptr<Base> B()
{
std::shared_ptr<Derived> result = std::make_shared<Derived>(Derived(1, 2));
return result;
}
std::shared_ptr<Base> C()
{
std::shared_ptr<Base> result = std::make_shared<Base>(Derived(1, 2));
return result;
}
std::shared_ptr<Base> D()
{
return std::make_shared<Base>(Derived(1, 2));
}
int main(int argc, char** argv)
{
// Works fine...
std::shared_ptr<Derived> resultA = std::dynamic_pointer_cast<Derived>(A());
// Works fine...
std::shared_ptr<Derived> resultB = std::dynamic_pointer_cast<Derived>(B());
// Does not cast to base? ...
std::shared_ptr<Derived> resultC = std::dynamic_pointer_cast<Derived>(C());
// Object returns fine (of type Base), but cannot be cast to Derived?
std::shared_ptr<Base> resultCBase = C();
std::shared_ptr<Derived> resultCDerived = std::dynamic_pointer_cast<Derived>(resultCBase);
// Does not cast to derived...
std::shared_ptr<Derived> resultD = std::dynamic_pointer_cast<Derived>(D());
return 0;
}
In summary:
Returning std::make_shared<T>
seems to work fine and allows caller to correctly cast. (see A()
).
Using make_shared<Derived>
to create a Derived, and then relying on implicit casting to return a shared_ptr<Base>
works and allows caller to correctly cast. (see B()
).
However for C()
and D()
when using make_shared<Base>(Derived(...))
, shared_ptr<Base>
is constructed (seems correct) but cannot cast to std::shared_ptr<Derived>
?
I'm not familiar with what make_shared<T>
gives (Although other answers of SO allude to better type safety and single allocation?), however it doesn't seem to perform or behave in the same manner as when replacing with std::shared_ptr<T>(new T(...))
?
Could someone please explain to me what is happening here and why it does not work as I expect (I presume I am using it wrong, or there is some subtle behaviour trait that I should know when using it)?
Since the above example has discrepancies, A()
& B()
working, but not C()
and D()
(assuming I am using it correctly)... why is make_shared<T>
recommended over std::shared_ptr<T>(new T(...))
, and are there any exceptions which would mean it is not recommended over the other?
I'm not familiar with what
make_shared<T>
gives
It creates a T
(on the heap, wrapping it in a shared_ptr
). It creates exactly and only T
. Your make_shared<Base>
calls are equivalent to new Base(Derived(1, 2)
. This will construct a Base
(by copying from the given Derived
's Base
subobject, commonly called "slicing", since you're only copying part of an object), because that's the type you gave it.
There is no Derived
in those shared_ptr
s, so you cannot dynamic_pointer_cast
to that type.
however it doesn't seem to perform or behave in the same manner as when replacing with
std::shared_ptr<T>(new T(...))
why is
make_shared<T>
recommended overstd::shared_ptr<T>(new T(...))
,
std::shared_ptr<Base>(new Base(Derived(1, 2)))
would have had the exact same problem. This has nothing to do with make_shared
and everything to do with you creating the wrong type.