(Original) smart pointer collection, original smart pointer collection
This article will introduce some details about the usage of smart pointers that may not be noticed at ordinary times (for the basic usage of smart pointers, refer to the previous blog post ).
1. A difference in the Construction of unique_ptr and shared_ptr
Unique_ptr supports dynamic arrays, while shared_ptr does not directly support dynamic arrays. Std: unique_ptr <int []> ptr (new int [10]); valid, while std: shared_ptr <int []> ptr (new int [10]); is invalid.
If you use std: shared_ptr to construct a dynamic array, You need to explicitly specify the deleteer, such as the following code:
Std: shared_ptr <int> p (new int [10], [] (int * p) {delete [] p ;}); // specify delete []
You can also use std: default_delete as the deleteer:
Std: shared_ptr <int> p (new int [10], std: default_delete <int []> );
We can encapsulate a make_shared_array method to make shared_ptr support Arrays:
template<typename T> shared_ptr<T> make_shared_array(size_t size){ return shared_ptr<T>(new T[size], default_delete<T[]>());}
Test code:
std::shared_ptr<int> p = make_shared_array<int>(10);std::shared_ptr<char> p = make_shared_array<char>(10);
Unique_ptr lacks a make_unique method similar to make_shared, but the make_unique method is added in c ++ 14. In fact, it is relatively simple to implement a make_unique method:
// Supports the normal pointer template <class T, class... Args> inlinetypename enable_if <! Is_array <T >:: value, unique_ptr <T >:: type make_unique (Args &&... args) {return unique_ptr <T> (new T (std: forward <Args> (args )...));} // supports dynamic array template <class T> inlinetypename enable_if <is_array <T>: value & extent <T >:: value = 0, unique_ptr <T> :: type make_unique (size_t size) {typedef typename remove_extent <T>: type U; return unique_ptr <T> (new U [size] ();} // filter out the template <class T, class... args> typena Me enable_if <extent <T>: value! = 0, void >:: type make_unique (Args &...) = delete;
The implementation is simple. If it is not an array, you can directly create the unique_ptr. If it is an array, first determine whether it is a fixed-length array. If it is a fixed-length array, the compilation fails; when an array is not a fixed-length value, the element type in the array is obtained, and then the unique_ptr of the dynamic array is created based on the input parameter size. Extent <T>: value is used to obtain the length of the array. If the obtained value is 0, it is not a fixed-length array.
2. A difference between shared_ptr and unique_ptr in the method of specifying the deleteer
Unique_ptr specifies that the delimiters and std: shared_ptr are different, for example, the following statement:
Std: shared_ptr <int> ptr (new int (1), [] (int * p) {delete p ;}); // correct
Std: unique_ptr <int> ptr (new int (1), [] (int * p) {delete p ;}); // Error
Std: unique_ptr: specifies the type of the delete tool when specifying the delete tool. Therefore, you cannot specify the delete tool directly like shared_ptr. You can write it like this:
Std: unique_ptr <int, void (*) (int *)> ptr (new int (1), [] (int * p) {delete p ;});
The preceding method is correct when lambda does not capture variables. If a variable is captured, an error is returned:
Std: unique_ptr <int, void (*) (int *)> ptr (new int (1), [&] (int * p) {delete p ;}); // error because the variable is captured
Why does lambda report an error when it captures a variable as the unique_ptr? If it does not capture a variable, it can be directly converted to a function pointer. If it does, it cannot be converted to a function pointer.
If you want the unique_ptr deleteer to support lambda, you can write as follows:
Std: unique_ptr <int, std: function <void (int *)> ptr (new int (1), [&] (int * p) {delete p ;});
We can also customize the unique_ptr deleteer, such as the following code:
#include <memory>#include <functional>using namespace std;struct MyDeleter{ void operator()(int*p) { cout<<"delete"<<endl; delete p; }};int main() { std::unique_ptr<int, MyDeleter> p(new int(1)); return 0;}3. Use smart pointers to manage memory allocated by third-party Libraries
The smart pointer can easily manage the memory dynamically allocated by the current library, and can also be used to manage the memory allocated by a third-party library. The memory allocated by a third-party library can be released only through the release interface provided by the third-party library. Since the pointers returned by the third-party library are generally original pointers, if the released interface of the third-party library is not called after use, this can easily cause memory leakage. For example, the following code:
void* p = GetHandle()->Create();//do something…GetHandle()->Release(p);
This code is actually insecure. When using the memory allocated by a third-party library, you may forget to call the Release interface, or you may accidentally return it, or an exception may occur in the middle, as a result, the Release interface cannot be called. It is appropriate to use a smart pointer to manage the memory of a third-party library. As long as the memory is out of scope, it will be automatically released without explicitly calling the Release interface, you don't have to worry about the problem of returning midway through or failing to call the Release Interface due to exceptions.
void* p = GetHandle()->Create();std::shared_ptr<void> sp(p, [this](void*p){ GetHandle()->Release(p);});
The above Code ensures that the memory allocated by third-party libraries can be correctly released at any time. Although it can solve the problem, it is still a little complicated, because every third-party library needs to call this code to allocate memory, which is complicated. We can extract this code as a public function, simplify calling.
std::shared_ptr<void> Guard(void* p){ return std::shared_ptr<void> sp(p, [this](void*p){ GetHandle()->Release(p);});}void* p = GetHandle()->Create();auto sp = Guard(p);//do something…
The above code is simplified through the Guard function, which is more convenient to use, but still not safe enough, because it may be written as follows:
Void * p = GetHandle ()-> Create (); Guard (p); // dangerous. After this sentence, p is released. // do something...
In this case, there is a problem with writing, which will lead to access the wild pointer, because Guard (p); is a right value. If no value is assigned to a pointer, Guard (p ); after this statement is completed, p is released, and the content of the wild pointer is accessed later. Auto sp = Guard (p); requires a value assignment operation. If you forget to assign a value, the pointer will be released in advance. This method is still not safe enough. We can define a macro to solve this problem:
# Define GUARD (P) std: shared_ptr <void> p # p (p, [] (void * p) {GetHandle () -> Release (p) ;}); void * p = GetHandle ()-> Create (); GUARD (p); // Security
You can also use unique_ptr to manage third-party memory:
#define GUARD(P) std::unique_ptr<void, void(*)(int*)> p##p(p, [](void*p){ GetHandle()->Release(p);});
Using macro definition, we can avoid the intelligent pointer forgetting to assign values, which is convenient and safe.
C ++ 11 boost technology exchange group: 296561497. You are welcome to exchange technologies.
Smart pointers (Resource Management)
You can add a smart pointer with the counting function to add the counter to the constructor, copy constructor, and assign value operator. In this way, whenever an object is constructed, assigned, and copied, the pointer counter is incremented by 1, and every time a pointer reference of the current object is lost (The Destructor is executed), the counter is reduced by 1, and every time the Destructor is executed, check the counter value. If it is zero, destroy the task. This is detailed in C ++ Primer (4th: Chinese Version 421-425, English version 494-499.
However, if you do not release a new class, the runtime environment will not release it, causing memory leakage. The operating system will not recycle its memory until the program exits. C ++ is different from Java in that C ++ has no garbage collection mechanism. Therefore, it is a good habit to correspond to new and delete.
What is the smart pointer in c ++?
When several objects share a resource, they point to the resource at the same time. If a common pointer is used, the shared resource will be destroyed during the analysis of an object. With a smart pointer, you don't have to worry about it. There is an object count in it, which indicates that several objects are using it, if one is destroyed, it is reduced by 1 until it is 0. If it is 0, it indicates that no object uses this resource and the resource storage zone is automatically released! There are many other smart places for you to watch online.