Introduction:
There are three kinds of smart pointers in c++ 11.
Shared pointer (shared_ptr)
Weak pointer (weak_ptr)
Unique pointer (unique_ptr)
They should be used only with heap memory, means only new constructor. if you use them with stack memory, a runtime error will occur.
They auto delete the object once they go out of scope (except weak pointer)
Avoid using raw pointers to refer to the same object. Don’t mix them together.
All are listed in <memory>
Shared pointer
Implements shared ownership, means any number of these smart pointers jointly own the object. The owned object is destroyed only when its last owning smart pointer is destroyed.
Once the managed object is gone, the shared_ptr = nullptr or 0.
A function can take and return a shared_ptr as value.
shared_ptr<Thing> do_something (shared_ptr<Thing> p)
class Thing {
public:
void func();
}
void foo () {
shared_ptr<Thing> p1 {new Thing}; // p1 owns Thing
shared_ptr<Thing> p1 = p1; // p1 and p2 share the ownership of Thing
p1 -> func(); //call member function like built in pointer
cout << *p1 // dereference just like built in pointer
p1.reset(); // decrement shared_ptr count, delete if required
p2 = nullptr; // decrement shared_ptr count, delete if required
}
shared_ptr<Thing> p1 = make_shared<Thing>(); //same as new, but efficient
Thing *raw_ptr = p1.get();
Thing *raw_ptr = sp; //Error
class Base { };
class Derived: public Base{ };
shared_ptr<Derived> dp1 {new Derived};
shared_ptr<Base> bp1 = dp1;
shared_ptr<Base> bp2 {dp1};
shared_ptr<Base> dp1 {new Derived};
shared_ptr<Base> base_ptr {new Base};
shared_ptr<Derived> derived_ptr;
derived_ptr = static_pointer_cast<Derived> (base_ptr)
Other casting functions: static_pointer_cast, dynamic_pointer_cast, const_pointer_cast
Two memory allocation, one for Thing object and one for manager object
created by the shared_ptr construction
same as new, but efficient. Only one memory allocation that is big enough to hold both the manager object and new object.
Weak Pointer
Weak pointers just observe the managed object. They do not keep it alive or affect its lifetime. So even if the last weak_ptr goes out of scope or disappear, the pointed-to object can still exist.
weak_ptr does not support * or -> (no dereference allowed).
Neither you can access the pointer to the object with it (No get() function available)
Example:
void do_something(weak_ptr<Thing> wp) {
shared_ptr<Thing> sp = wp.lock(); //get shared_ptr from weak_ptr
if(sp) { //do your stuff }
else { // Thing object is gone }
}
Initializing a weak_ptr
Default value is empty
You can point a weak_ptr to an object only by copy or assignment from a shared_ptr or an existing weak_ptr to the object.
Unlike shared_ptr, you can not reset a weak_ptr by assignment to nullptr. Use reset() function to set a weak_ptr back to the empty state in which it is pointing to nothing.
shared_ptr<Thing> sp1 {new Thing};
weak_ptr<Thing> wp1 {sp1}; //construct wp1 from a shared_ptr
weak_ptr<Thing> wp2; // empty weak_ptr pointing to nothing
wp2 = sp1; // wp2 now points to new Thing object
weak_ptr<Thing> wp3 {wp2}; //construct wp3 from a weak_ptr
weak_ptr<Thing> wp4;
wp4 = wp2; //wp4 also now points to the same new Thing object
shared_ptr<Thing> sp = wp.lock(); //get shared_ptr from weak_ptr
Note: You can not refer to the object directly with a weak_ptr (already
mentioned in the second bullet of this weak_ptr section), you need to get a shared_ptr from it first with lock() function (already mentioned in the fourth bullet of this weak_ptr section).
The lock() function examines the state of the manager object to determine whether the managed object still exists, and provides an empty shared_ptr if it does not, and a shared_ptr if it does. See example in the fourth bullet of this weak_ptr section).
Key points:
weak_ptr does not support * or -> (no dereference allowed).
No get() function available
lock() function to get shared_ptr
No nullptr assignment allowed, use reset() function.
Unique Pointer
An object is owned by exactly one unique_ptr.
Unlike shared_ptr or built-in pointer, you can not copy or assign a unique_ptr to another unique_ptr.
When the unique_ptr goes out of scope, the pointed-to object gets deleted and this happens regardless of how we leave the function, either by a return or an exception being thrown somewhere.
void foo() {
unique_ptr<Thing> p { make_unique<Thing> }; // p owns the Thing
p -> do_something();
another_function(); // might throw an exception
} // p gets destroyed. Destructor destroys the Thing.
unique_ptr<Thing> p1 { make_unique<Thing> }; // p owns the Thing
unique_ptr<Thing> p2 {p1}; //error, copy-construction is not allowed
unique_ptr<Thing> p3; //an empty unique_ptr
p3 = p1; //error, copy assignment is not allowed
unique_ptr<Thing> create_Thing() {
unique_ptr<Thing> tmp_ptr { new Thing }
return tmp_ptr; //tmp_ptr will surrender ownership
}
void foo () {
unique_ptr<Thing> p1 { create_Thing() }; // p1 owns the Thing
unique_ptr<Thing> p2; // default unique_ptr; owns nothing
p2 = create_Thing(); // p2 now owns the second Thing
}
unique_ptr<Thing> p1 {new Thing}; // p1 owns the Thing
unique_ptr<Thing> p2; // p2 owns nothing
// invoke move assignment explicitly
p2 = std::move(p1); // now p2 owns it, p1 owns nothing
// invoke move construction explicitly
unique_ptr<Thing> p3 {std::move(p2)}; //now p3 owns it, p1 and p2 own nothing
Use reset() and nullptr in the same way as shared_ptr
Use make_unique for memory allocation instead of new. It is especially designed for unique_ptr. The reasoning is same for both unique_ptr and shared_ptr.
Reference:
Kieras, D.: Using C++11’s Smart Pointers. University of Michigan. Tutorial (2016) 1 - 14