Reputation: 11
I had a scenario in C++ that calls the child's destructor in a case where I didn't expect it. A minimal repro is below:
#include <cstdio>
#include <memory>
using namespace std;
class Parent {
public:
};
class Child : public Parent {
public:
~Child() {
printf("Got here\n");
}
};
int
main()
{
shared_ptr<Parent> x(new Child);
}
Usually something like this is a bug. The developer intends that the child destructor is called, and the correct action would be to insert into the parent an empty virtual destructor. However, to my shock, both G++ 4.4.7 (yeah, I know it's old) and clang 3.4.2 compile this such that the child destructor is called.
Does this conform to the standard?
Upvotes: 0
Views: 84
Reputation: 275976
A unique ptr would screw up here. But shared ptr does some "magic" here.
shared_ptr<T> has two things they manage; the
T*` and the reference counting block.
The reference counting block contains two atomic<std::size_t>
s, one for strong references and one for weak references, and a type-erased deleter. This std::function
-like type-erased deleter remembers how to delete the thing you own.
When you construct any shared_ptr
with a U*u
, it by default stores [u]{std::default_delete<U>{}(u);}
in that deleter.
In effect, it remembers how to delete the object based on the type passed in.
shared_ptr
is exceedingly flexible.
You can pass in a custom deleter to replace the default one, you can use the aliasing constructor to split the reference counting block used from the T*
stored, you can use make_shared
to allocate the reference counting block and a T
in the same memory allocation.
The overhead of the reference counting block is why it stores the deleter; it wasn't viewed as overly expensive, given that we needed the block anyhow. In comparison, unique_ptr
by default does no such thing; you have to explicitly add a deleter, and you'd have to manage all of the fancy tricks that shared_ptr does for you by default if you wanted them. unique_ptr
has basically zero overhead over a raw owning pointer; shared_ptr
has noticable overhead, but small compared to memory allocation overhead usually.
Upvotes: 0
Reputation: 96311
Well even if shared_ptr
didn't have special magic, delete
ing by parent pointer with non-virtual destructor is just undefined behavior so the results (of calling the child destructor) would definitely be conforming.
But in this case shared_ptr
"remembers" the type of the original object you passed into it and destroys it by child pointer (through its stored deleter).
Upvotes: 5