This tutorial deals with C++ 11’s smart pointer, which consists unique_ptr, shared_ptr, and its partner, weak_ptr.
本文摘自David Kieras, EECS Department, University of Michigan,Using C++ 11’s Smart Pointers. 建议阅读原文
Smart pointers are class objects that behave like bulit-in pointers but also manage objects that you create with new.
In a C++ 11 implementation, the following #include is all that is needed
#include
The shared_ptr class template is a refereced-counted smart pointer.
* managed object
* manager object
注释:在用new operator初始化一个shared_ptr时,会产生一个manager object来管理生成的managed object。
If the weak count is greater than zero, the manager object is kept. If the weak count is decremented to zero, and the shared count is also zero, the weak_ptr destructor deletes the manager object. Thus the managed object stays around as long as there are shared_ptrs pointing to it, and the manager object stays around as long as there are either shared_ptrs or weak_ptrs referring to it.
If the pointer and shared count are non-zero, then the managed object is still present, and weak_ptr can make the pointer to it available. This is done by a weak_ptr member function that creates and returns a new shared_ptr to the object; the new shared_ptr increments the shared count, which ensures that the managed object will stay in existence as long as necessary
You must ensure that there is only one manager object for each managed object. You do this by writing your code so that when an object is first created, it is immediately given to a shared_ptr to manage , and any other shared_ptrs or weak_ptrs that are needed to point to that object are all directly or indirectly copied or assigned from that first shared_ptr. The customary way to ensure this is to write the new object expression as the argument for a shared_ptr constructor, or use the make_shared function template described below. (RAII, use a foolproof way to ensure you create smart pointers in right way)
If you want to get the full benefit of smart pointers, your code should avoid using raw pointers to refer to the same objects; otherwise it is too easy to have problems with dangling pointers or double deletions. In particular, smart pointers have a get() function that returns the pointer member variable as a built-in pointer value. This function is rarely needed. As much as possible, leave the built-in pointers inside the smart pointers and use only the smart pointers. (never use raw pointers when you wrap it with a smarter pointer, unless you have a good reason)
注释:scott meyer的effective c++系列有讲到什么时候会用raw pointer.
can be used as a call-by-value function argument or return value, or stored in containers. You can also reset a shared_ptr by assigning it the value nullptr, which is converted to an empty shared_ptr before the assignment (decrease count in advance, so may release memory in advance).
注释:下面是shared_ptr的基本用法。
#include
#include
using namespace std;
class Widget {
public:
void foo();
};
ostream& operator<< (ostream&, const Widget&);
// a function can return a shared_ptr
shared_ptr bar();
// a function can take a shared_ptr by value
shared_ptr do_something_with(shared_ptr p);
void foo()
{
shared_ptr p1(new Widget()); // the new is in the shared_ptr constructor expression
shared_ptr p2 = p1; // p1 and p2 now share ownership of the Widget
shared_ptr p3(new Widget()); // another Widget
p1 = bar(); // p1 may no longer pointer to the first thing
do_something_with(p2);
p3->foo(); // call a member funtion like built-in pointer
cout << *p2 << endl; // deference like built-in pointer
p1.reset(); // reset with a member funtion or assignment to nullptr
p2 = nullptr; // convert nullptr to an empty shared_ptr, and decrement count
}
The constructors and assignment operators in shared_ptr (and weak_ptr) are defined with templates so that if the built-in pointers could be validly copied or assigned, then the corresponding shared_ptrs can be also:
class Base {};
class Derived : public Base {};
shared_ptr<Derived> dp1(new Derived());
shared_ptr bp1 = dp1;
shared_ptr bp2(dp1);
shared_ptr bp3(new Derived());
Should define virtual base destructor
注释:shared_ptr与raw pointer基本有着相同的行为,这里跑题一下,绝对不要在数组中使用多态。
C++11 supplies some function templates that provide a casting service corresponding to the built-in pointer casts. These functions internally call the get() function from the supplied pointer, perform the cast, and return a shared_ptr of the specified type.
shared_ptr base_ptr (new Base);
shared_ptr derived_ptr;
// if static_cast(base_ptr.get()) is valid, then the following is vaild:
derived_ptr = static_pointer_cast(base_ptr);
You can compare two shared_ptrs using the ==, !=, and < operators; they compare the internal raw pointers and so behave just like these operators between built-in pointers. In addition, a** shared_ptr provides a conversion to a bool**. (operator int() or may other…)
shared_ptr p(new Widget());
There are actually ***two dynamic memory allocations*** that happen: one for the object itself from the new, and then a second for the manager object created by the shared_ptr constructor.
To address this problem, C++11 includes a function template make_shared that does a single memory allocation big enough to hold both the manager object and the new object, passing along any constructor parameters that you specify, and returns a shared_ptr of the specified type, which can then be used to initialize the shared_ptr that you are creating (with efficient move semantics).
So instead of:
shared_ptr p(new Thing); // ouch - two allocations
you would write:
shared_ptr<Thing> p(make_shared<Thing>()); // only one allocation!
Using make_shared also avoids explicit use of new, promoted in the slogan “no naked new!” That is, if you are using shared_ptrs throughout a project, never writing new, and always using make_shared, is an easy way to make sure that all allocated objects are managed by shared_ptrs.
You can’t dereference it; neither operator* nor operator-> is defined for a weak_ptr. You can’t access the pointer to the object with it - there is no get() function. There is a comparison function defined so that you can store weak_ptrs in an ordered container; but that’s all.
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.
注释:以下是weak_ptr的用法,用weak_ptr可以解决shared_ptr环的问题,这个代码我会补上。
shared_ptr wp1(sp); // construct wp1 from a shared_ptr
weak_ptr wp2; // an empty weak_ptr - points to nothing
wp2 = sp; // wp2 now points to the new Widegt
weak_ptr wp3(wp2); // construct wp3 from a weak_ptr
weak_ptr wp4;
wp4 = wp2; // wp4 now points to the new Widegt
You can use the reset() member function to set a weak_ptr back to the empty state in which it is pointing to nothing. (Unlike shared_ptr, you can’t reset a weak_ptr by assignment to nullptr)
void do_it(weak_ptr wp) {
shared_ptr sp = wp.lock(); // get shared_ptr from weak_ptr
if(sp)
sp->foo();
else
cout << "The Widget is gone!" << endl;
}
This is the most useful and common way to use a weak_ptr to access the object.注释:这个安全。
2.
bool is_it_there(weak_ptr wp) {
if(wp.expired()) {
cout << "The thing is gone!" << endl;
return false;
}
return true
}
This approach is useful as a way to simply ask whether the pointed-to object still exists. Notice that if after calling expired(), the code goes on to use lock() to get a shared_ptr to the object, testing first for expired() is redundant and may actually be problematic.
3.
We can construct a shared_ptr from a weak_ptr; if the weak_ptr is expired, an exception is thrown, of type
std::bad_weak_ptr. This has its uses, but the first method is generally handier and more direct.
The unique ownership is enforced by disallowing (with =delete) copy construction and copy assignment.
**the move constructor and move assignment operator are defined for unique_ptr **so that they transfer ownership from the original owner to the new owner. After move construction, the newly created unique_ptr owns the object and the original unique_ptr owns nothing.
unique_ptr<Widget> p1(new Widget()); // p1 owns the Widget
unique_ptr<Widget> p2; // p2 owns nothing
// invoke move assignment explicitly
p2 = std::move(p1); // now p2 owns it, p1 onws nothing
// invoke move construction explicitly
unique_ptr<Widget> p3(std::move(p2)); // now p3 owns it, p2 and p1 own nothing
注释:使用uniqe_ptr是IMPL手法的一个良好实现,在move constructor中直接move所指向的资源,另外uniqe_ptr也可以替换copy-swap idiom,真是太棒了!
The most common use of unique_ptr is as a pretty fool-proof way of making sure an object allocated in a function (or class constructor) gets deleted. However, there are situations in which ownership of objects needs to be transferred around but always remains in one place at a time; unique_ptr gives you way to represent this concept directly.