OK, so first some things that might be relevant:
I'm using the Clang 3.1 compiler, in C++11 mode, with the standard library set to libc++.
I'm trying to familiarize myself with C++11, and in so doing I ran across behavior that seems odd. It may be a quirk of Clang or libc++ but I can't speak C++ standardese and I have no access to other compilers with C++11 support so I can't really check it, and I've searched the internet and Stack Overflow to the best of my ability without finding anything related...so here we go:
When using shared_ptr / unique_ptr to implement RAII for a simple resource, it seems that their behavior differs with respect to null pointers upon deletion. I realize that normally it's not necessary to delete a null pointer, but I had expected the behavior to at least match between the two STL smart pointers.
For the specific case, consider the following code:
{ auto Deleter = [](void *){cout << "It's later!" << endl;}; shared_ptr<void> spDoSomethingLater(nullptr, Deleter); unique_ptr<void, void (*)(void *)> upDoSomethingLater(nullptr, Deleter); cout << "It's now!" << endl; }
I would have expected one of the following outputs from this:
a) If both deleters are called even though the pointer is null:
"It's now!" "It's later!" "It's later!"
b) If neither deleter is called because the pointer is null:
"It's now!"
But I observe neither of these cases. Instead, I observe:
"It's now!" "It's later!"
Which means one but not the other of the deleters is being called. Upon further investigation, I found that the deleter for shared_ptr is called regardless of whether it holds a null value, but unique_ptr's deleter is only called if it does not hold a null value.
My questions: Is this actually the correct behavior as specified by the standard? If so, why does the specified behavior differ between the two STL types in this manner? If not, is this a bug I should report to libc++?
Use unique_ptr when you want to have single ownership(Exclusive) of the resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another. A shared_ptr is a container for raw pointers.
Use unique_ptr when you want a single pointer to an object that will be reclaimed when that single pointer is destroyed. Use shared_ptr when you want multiple pointers to the same resource.
false if the shared_ptr is a null pointer.
std::unique_ptr s are useful when you want to tie the lifetime of the object to a particular block of code, or if you embedded it as member data inside another object, the lifetime of that other object. The object exists until the containing block of code is exited, or until the containing object is itself destroyed.
The observed behavior is in accordance with the standard.
For unique_ptr
, 20.7.1.2.2/2 (destructor effects) says
Effects: If
get() == nullptr
there are no effects. Otherwiseget_deleter()(get())
.
For shared_ptr
, 20.7.2.2.2/1 says that the deleter should be called even if it wraps the null pointer:
Effects:
- If *this is empty or shares ownership with another
shared_ptr
instance (use_count() > 1
), there are no side effects.- Otherwise, if *this owns an object
p
and a deleterd
,d(p)
is called.- Otherwise, *this owns a pointer
p
, anddelete p
is called.
The important detail here is the expression "owns an object p
". 20.7.2.2/1 says that "a shared_ptr
object is empty if it does not own a pointer". 20.7.2.2.1/9 (the relevant constructor) says that it "constructs a shared_ptr
object that owns the object p
and the deleter d
".
So as far as I can tell, that invocation technically makes the shared_ptr
own the null pointer, which results in the deleter being called. Contrast this with the parameterless constructor which is said to leave the shared_ptr
"empty".
Yes it is the correct behavior.
§20.7.1.2.2[unique.ptr.single.dtor]/2:
unique_ptr
destructorEffects: If
get() == nullptr
there are no effects. Otherwiseget_deleter()(get())
.
§20.7.2.2.2[util.smartptr.shared.dest]/1:
shared_ptr
destructorEffects:
- If
*this
is empty or shares ownership with anothershared_ptr
instance (use_count() > 1
), there are no side effects.- Otherwise, if
*this
owns an objectp
and a deleterd
,d(p)
is called.- Otherwise,
*this
owns a pointerp
, and deletep
is called.
So there should be no effect since the shared_ptr is empty? Wrong, because providing the a pointer makes the shared_ptr not empty, even if the pointer is null.
§20.7.2.2.1[util.smartptr.shared.const]/8–10
shared_ptr
constructorstemplate<class Y, class D> shared_ptr(Y* p, D d); template<class Y, class D, class A> shared_ptr(Y* p, D d, A a); template <class D> shared_ptr(nullptr_t p, D d); template <class D, class A> shared_ptr(nullptr_t p, D d, A a);
Requires:
p
shall be convertible toT*
. …Effects: Constructs a
shared_ptr
object that owns the objectp
and the deleterd
.Postconditions:
use_count() == 1 && get() == p
.
This means the shared_ptr owns the nullptr. Thus the deleter will be called when the shared_ptr is destroyed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With