Smart pointer [C + +]
RALL mechanism: resource acquisition is initialization, and the technology of using = = local object = = to manage resources (resources here mainly refer to the limited resources in the operating system, such as memory, network socket, mutex, file handle, etc. local object refers to the object stored in the stack, and its physiological cycle is managed by the operating system)
1. Problems with using bare pointers
- When used, we cannot judge whether it points to an object or a group of objects.
int *tmp;// Field pointer
int *op=NULL;// Null pointer
//The pointer from heap new is released later, which is called invalid pointer (or dangling pointer)
- It is impossible to determine whether a used pointer should be destroyed, because it is impossible to determine whether the pointer still "owns" the object pointed to at this time.
- When the pointer is destroyed, it cannot be determined whether to delete it with the delete keyword or whether there is another destruction mechanism.
- There is no guarantee that the path after code destruction may be destroyed again: the omission of any path may lead to memory leakage.
- Theoretically, it is impossible to judge whether the pointer is suspended.
2. Several smart pointers in C + +
- auto_ptr (removed in C++17) (C11 deprecated)
- unique_ptr unique smart pointer
- shared_ptr shared smart pointer
- weak_ Pointer to PTR weak reference
C + + covers: ① Part C + ② class + ③ template
Smart pointers generally do not deal with const type objects. If there are special needs, modify the smart pointer itself.
3,auto_ptr
auto_ When PTR is constructed, it will acquire the ownership of an object and release the object when it is destructed.
It is generally used as follows:
int *p=new int(10); auto_ptr<int> op(p);
In this way, we don't have to worry about when the pointer p is released, and we don't have to worry about memory leakage caused by exceptions.
However, using auto_ptr may have such problems:
- If there are shallow copies that share the same object resources, there will be a problem with destruct release.
int *p= new int(10); auto_ptr<int> op1(p); auto_ptr<int> op2(p);
In this example, there is a problem, because both op1 and op2 think it is necessary to manage pointer p, which will cause two releases of pointer p, which will lead to problems.
Look at this situation again:
- It is not clear whether it points to an object or a group of objects.
string *str=new string[10]; auto_ptr<string> op(str);
In this example, auto_ptr destructor uses delete instead of delete [], so auto_ptr also cannot manage an array pointer.
- The explicit keyword of the constructor effectively prevents the implicit conversion from a "bare" pointer to auto_ptr type.
- Copy and assignment
A "bare" pointer cannot be used by more than two auto at the same time_ PTR has, so this problem should be taken into account when copying or assigning values. auto_ptr's approach is "ownership transfer":
int *p = new int(10); auto_ptr<int> op1(p); auto_ptr<int> op2=op1; cout<<*op1<<endl;//error because op1 has lost ownership of p
Similarly, auto_ptr cannot be passed by value as a function parameter:
void func(auto<int> op) { cout<<*op<<endl; } int main() { auto_ptr<int> op1(new int(10)); func(op1); cout<<*op1<<endl; }
Because a local object will be generated during the function call to receive the incoming auto_ptr (copy construction). At this time, the real argument loses the ownership of its own object, but this local auto_ptr will automatically destruct at the end of the function and delete the object. Therefore, do not use auto_ptr is passed as a parameter by value.
For another example, we have two classes:
class Object{}; class Base:public Object{}; auto_ptr<Object> op1=auto_ptr<Base>(new Base);//From auto_ PTR < base > to auto_ Implicit conversion of PTR < Object >
- If the copy is constructed to transfer the object resource, but there is a problem in copying the parameters passed by the calling function, the main function argument loses the ownership of the object resource (because it will be destructed after the function call).
Before C++11, there was no concept of moving assignment, so it is still used, but there will be problems when using it now.
4,unique_ptr unique smart pointer
It is a smart pointer defined in,
characteristic:
- It is not allowed to initialize another object with one object (the copy structure is deleted), and assignment is not allowed (the assignment method is deleted)
- It "exclusively" owns the object it points to, two unique objects_ PTR cannot point to the same object
- If you need to initialize another object with one object, you can explicitly use std::move() / / to move the structure
- unique_ptr object holds a pointer to an object. When it is deleted or left its scope, it will automatically release the resources occupied by the object
Unnamed object = = right value object
unique_ Use of PTR:
unique_ptr<int> up(new int(10)); cout<<*up<<endl; unique_ptr<int> up2=up1;//report errors unique_ptr<int> up3(up1);//report errors unique_ptr<int> up4=std::move(up1);//OK transfer ownership unique_ptr<int> up5(std::move(up4);//OK
unique_ptr does not allow copying, but there is one exception: you can return a unique from a function_ ptr
unique_ptr<int> clone(int a) { unique_ptr<int> res(new int(a)); return res; } int main() { int a=5; unique_ptr<int> pa=clone(a); unique_ptr<int> pb; pb=clone(a); cout<<*pa<<endl; cout<<*pb<<endl; return 0; }
5,shared_ptr shared smart pointer
shared_ptr is a reference counting smart pointer used to share ownership of objects, that is, it allows multiple pointers to the same object.
It uses a reference count pointer to count. (Note: if each shared pointer is copied, it shares the same reference count pointer)_ When the counter of PTR object becomes 0, it will automatically release the objects it manages.
After C11, the type has several default functions (8): 6 + 2 (constructor, destructor, copy construction, assignment, address taking, constant address taking, moving copy construction, moving assignment)
- Check reference count: unique() and use_ count(),use_ The count () function may be inefficient. It should only be used for testing or debugging, and the unique () function is used to test the shared_ Whether PTR is the only owner of the original pointer (that is, when use_count()==1, return true, otherwise return false).
shared_ Memory distribution of PTR object:
Using shared_ptr object, there will be another problem:
class Child; class Parent { public: shared_ptr<Child> child; ~Parent() { cout << "~Parent()" << endl; } void hi()const { cout << "hello" << endl; } }; class Child { public: shared_ptr<Parent> parent; ~Child() { cout << "~Child()" << endl; } }; int main() { shared_ptr<Parent> p = make_shared<Parent>(); shared_ptr<Child> c = make_shared<Child>(); p->child = c; c->parent = p; c->parent->hi(); return 0; }
You can see that after the program runs, only hello is printed, but ~ Parent() and ~ Child() are not printed, indicating that the destructors of these two classes have not been called.
When the program exits, even if the parent and child are destroyed, only the reference count becomes 1, so the p and c objects are not destroyed.
At this point, we introduce weak_ptr weak reference pointer:
6,weak_ptr weak reference smart pointer
It is to cooperate with shared_ptr is a smart pointer introduced by PTR. It points to a shared pointer_ PTR manages the object without affecting the life cycle of the object, that is, a break_ PTR is bound to a shared_ptr, will not change shared_ Reference count for PTR.
Since weak_ptr does not change the shared_ The reference count of the PTR instance, there may be a weak_ The object pointed to by PTR is released. In this case, use the lock() function to judge the weak_ Whether the object pointed to by PTR exists.
class Child; class Parent { public: weak_ptr<Child> child; ~Parent() { cout << "~Parent()" << endl; } void hi()const { cout << "hello" << endl; } }; class Child { public: weak_ptr<Parent> parent; ~Child() { cout << "~Child()" << endl; } }; int main() { shared_ptr<Parent> p = make_shared<Parent>(); shared_ptr<Child> c = make_shared<Child>(); p->child = c; c->parent = p; c->parent.lock()->hi(); return 0; }