Home  >  Article  >  Backend Development  >  C++11 new features smart pointers (shared_ptr/unique_ptr/weak_ptr)

C++11 new features smart pointers (shared_ptr/unique_ptr/weak_ptr)

高洛峰
高洛峰Original
2017-01-23 14:00:531959browse

Basic usage of shared_ptr

shared_ptr uses reference counting to manage the pointed object. When there is a new shared_ptr pointing to the same object (copying the shared_ptr, etc.), the reference count is increased by 1. When shared_ptr goes out of scope, the reference count is decremented by 1. When the reference count reaches 0, the managed memory is released.

The advantage of this is that it relieves programmers from the pressure of manually releasing memory. In the past, in order to handle exceptions in the program, it was often necessary to manually encapsulate the pointer into a class and use the destructor to release the dynamically allocated memory; now this process can be left to shared_ptr.

Generally we use make_shared to obtain shared_ptr.

cout<<"test shared_ptr base usage:"<<endl;
shared_ptr<string> p1 = make_shared<string>("");
if(p1 && p1->empty())
*p1 = "hello";
 
auto p2 = make_shared<string>("world");
cout<<*p1<<&#39; &#39;<<*p2<<endl;
 
cout<<"test shared_ptr use_count:"<<endl;
cout<<"p1 cnt:"<<p1.use_count()<<"\tp2 cnt:"<<p2.use_count()<<endl;
 
auto p3 = p2;
cout<<"p1 cnt:"<<p1.use_count()<<"\tp2 cnt:"<<p2.use_count()<<"\tp3 cnt:"<<p3.use_count()<<endl;
p2 = p1;
cout<<"p1 cnt:"<<p1.use_count()<<"\tp2 cnt:"<<p2.use_count()<<"\tp3 cnt:"<<p3.use_count()<<endl;

shared_ptr and new

shared_ptr can be initialized using a pointer returned by a new expression.

cout<<"test shared_ptr and new:"<<endl;
shared_ptr<int> p4(new int(1024));
//shared_ptr<int> p5 = new int(1024); // wrong, no implicit constructor
cout<<*p4<<endl;

However, the pointer returned by a new expression cannot be assigned to shared_ptr.

In addition, special attention should be paid to not mixing new and shared_ptr!

void process(shared_ptr<int> ptr)
{
cout<<"in process use_count:"<<ptr.use_count()<<endl;
}
 
cout<<"don&#39;t mix shared_ptr and normal pointer:"<<endl;
shared_ptr<int> p5(new int(1024));
process(p5);
int v5 = *p5;
cout<<"v5: "<<v5<<endl;
 
int *p6 = new int(1024);
process(shared_ptr<int>(p6));
int v6 = *p6;
cout<<"v6: "<<v6<<endl;

The above program fragment will output:

in process use_count:2
v5: 1024
in process use_count:1
v6: 0
It can be seen that when process p6 is used for the second time, the reference count of shared_ptr is 1, and when leaving When the scope of process is reached, the corresponding memory will be released, and p6 becomes a hanging pointer.

So, once the pointer returned by a new expression is managed by shared_ptr, do not access this memory through ordinary pointers!

shared_ptr.reset

shared_ptr can be reset to point to another object through the reset method. At this time, the reference count of the original object is reduced by one.

cout<<"test shared_ptr reset:"<<endl;
cout<<"p1 cnt:"<<p1.use_count()<<"\tp2 cnt:"<<p2.use_count()<<"\tp3 nt:"<<p3.use_count()<<endl;
p1.reset(new string("cpp11"));
cout<<"p1 cnt:"<<p1.use_count()<<"\tp2 cnt:"<<p2.use_count()<<"\tp3 cnt:"<<p3.use_count()<<endl;
shared_ptr deleter

You can customize a delete function to be called when shared_ptr releases the object.

void print_at_delete(int *p)
{
cout<<"deleting..."<<p<<&#39;\t&#39;<<*p<<endl;
delete p;
}
 
cout<<"test shared_ptr deleter:"<<endl;
int *p7 = new int(1024);
shared_ptr<int> p8(p7, print_at_delete);
p8 = make_shared<int>(1025);

Basic usage of unique_ptr

unique_ptr is exclusive to the object pointed to, as its name suggests. Therefore, unique_ptr cannot be copied, assigned, etc., but control can be transferred between unique_ptr through the release function.

cout<<"test unique_ptr base usage:"<<endl;
unique_ptr<int> up1(new int(1024));
cout<<"up1: "<<*up1<<endl;
unique_ptr<int> up2(up1.release());
cout<<"up2: "<<*up2<<endl;
//unique_ptr<int> up3(up1); // wrong, unique_ptr can not copy
//up2 = up1; // wrong, unique_ptr can not copy
unique_ptr<int> up4(new int(1025));
up4.reset(up2.release());
cout<<"up4: "<<*up4<<endl;

unique_ptr as parameter and return value

There are two special cases for the above restrictions on copying, that is, unique_ptr can be used as the return value of the function Values ​​and parameters are used. Although there is an implicit copy at this time, it is not unfeasible.

unique_ptr<int> clone(int p)
{
return unique_ptr<int>(new int(p));
}
 
void process_unique_ptr(unique_ptr<int> up)
{
cout<<"process unique ptr: "<<*up<<endl;
}
 
cout<<"test unique_ptr parameter and return value:"<<endl;
auto up5 = clone(1024);
cout<<"up5: "<<*up5<<endl;
process_unique_ptr(move(up5));
//cout<<"up5 after process: "<<*up5<<endl; // would cause segmentfault

The std::move function here will be discussed in detail later^_^

unique_ptr deleter

unique_ptr can also set deleter. Unlike shared_ptr, it needs to specify the type of deleter in the template parameter. Fortunately, we have the powerful tool decltype, otherwise it would be troublesome to write.

cout<<"test unique_ptr deleter:"<<endl;
int *p9 = new int(1024);
unique_ptr<int, decltype(print_at_delete) *> up6(p9, print_at_delete);
unique_ptr<int> up7(new int(1025));
up6.reset(up7.release());

weak_ptr

weak_ptr is generally used in conjunction with shared_ptr. It can point to the object pointed to by shared_ptr, but it does not increase the reference count of the object. In this way, it is possible that the object pointed to by weak_ptr has actually been released. Therefore, weak_ptr has a lock function that attempts to retrieve a shared_ptr pointing to the object.

cout<<"test weak_ptr basic usage:"<<endl;
auto p10 = make_shared<int>(1024);
weak_ptr<int> wp1(p10);
cout<<"p10 use_count: "<<p10.use_count()<<endl;
//p10.reset(new int(1025)); // this will cause wp1.lock() return a false obj
shared_ptr<int> p11 = wp1.lock();
if(p11) cout<<"wp1: "<<*p11<<" use count: "<<p11.use_count()<<endl;

Summary

shared_ptr uses reference counting to manage the pointed objects.
Shared_ptr can be initialized using a pointer returned by a new expression; however, a pointer returned by a new expression cannot be assigned to shared_ptr.
Once the pointer returned by a new expression is managed by shared_ptr, do not access this memory through ordinary pointers.
Shared_ptr can be reset to point to another object through the reset method. At this time, the reference count of the original object is reduced by one.
You can customize a delete function to be called when shared_ptr releases the object.
unique_ptr is exclusive to the object pointed to.
Unique_ptr cannot be copied, assigned, etc., but control can be transferred between unique_ptr through the release function.
unique_ptr can be used as the return value and parameter of the function.
unique_ptr can also set deleter, and the type of deleter needs to be specified in the template parameters.
Weak_ptr is generally used in conjunction with shared_ptr. It can point to the object pointed to by shared_ptr, but it does not increase the object's reference count.
Weak_ptr has a lock function that tries to retrieve a shared_ptr pointing to the object.

The above is the entire content of this article. I hope it will be helpful to everyone's learning. I also hope that everyone will support the PHP Chinese website.

For more articles related to the new features of C++11, smart pointers (shared_ptr/unique_ptr/weak_ptr), please pay attention to the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn