In depth study of C + + - 44 smart pointer
We all know that new is used to allocate memory on the heap and delete is used to free memory, because it will not free memory automatically. Smart pointer is essentially a wrapper class of the original pointer. When a smart pointer is created, it will call new and allocate memory for it. Based on this smart pointer, these memory will be released automatically at a certain time. Here are three kinds of smart pointers
When using smart pointers, include the memory header file
Scope pointer unique_ptr
unique_ptr is the scope pointer. It will be destroyed when the scope is exceeded, and then delete is invoked.
We can't copy a unique_ptr, because if you copy a unique_ptr, then they will point to the same memory block. If one of them dies, it will release that memory, and another pointer to the same memory will point to the memory that has been released.
We first create a class to understand smart pointers. The class only contains constructors and destructors
class Entity { public: Entity() { std::cout << "Created Entity" << std::endl; } ~Entity() { std::cout << "Destroyed Entity" << std::endl; } void Print() {} };
Create a unique under a specific scope_ PTR: create a pair of braces in main, and the empty scope is inside the braces. Use unique in it_ PTR to assign Entity:
int main() { { std::unique_ptr<Entity> entity = new Entity(); //error } }
This structure will report an error because it is unique_ The constructor of PTR is explicit and needs to be explicitly called. There is no implicit conversion of the constructor, so it should:
int main() { { std::unique_ptr<Entity> entity(new Entity()); //correct } }
However, the following method is more recommended! The reason is for abnormal safety. If the constructor happens to throw an exception, use make_unique ensures that you don't end up with a dangling pointer without a reference, resulting in a memory leak.
int main() { { std::unique_ptr<Entity> entity = std::make_unique<Entity>(); } }
Then you can use the smart pointer just like the original pointer, such as using the arrow operator to call the function:
int main() { { std::unique_ptr<Entity> entity = std::make_unique<Entity>(); entity->Print(); } }
Through one-step debugging, you can see that the program outputs Created Entity when entering the scope {and Destroyed Entity when leaving the scope}, that is, the automatic creation and destruction of smart pointers.
If you need to copy or share this pointer, so that this pointer can be passed to a function or a class, unique_ptr will not be available. If you try to copy a unique_ptr:
int main() { { std::unique_ptr<Entity> entity = std::make_unique<Entity>(); std::unique_ptr<Entity> e0 = entity; } }
This will report an error, because in unique_ In the definition of PTR, the copy constructor and copy construction operator are deleted because this is not allowed. This is to prevent you from jumping into the pit because one of them is unique_ When PTR dies, the underlying memory of the heap allocation object will be released and another unique_ptr will point to this nonexistent memory. So shared pointer appears_ ptr:
Shared pointer_ ptr
Shared pointer_ PTR is more awesome, shared_ The way PTR is implemented actually depends on the compiler and the standard library you use in the compiler. In most cases, it uses reference counting. Reference counting is basically a method to track how many references your pointer has. Once the reference count reaches 0, it will be deleted. If I create a shared_ptr, another shared is created_ PTR to copy it. At this time, the reference count is 2. When the first shared_ When PTR dies, the reference count decreases by 1 and becomes 1. When the last shared_ptr is also dead. When the reference count becomes 0, the pointer will be destroyed and the memory will be released.
But don't write like this!!!
int main() { { std::shared_ptr<Entity> entity(new Entity()); } }
In unique_ The reason why new is not called directly in PTR is because it is abnormally safe, but in shared_ptr is different. Because shared_ptr needs to allocate another block of memory, called the control block, to store the reference count. If you use new to create an Entity and pass it to shared_ptr constructor, then it must allocate memory twice: allocate new Entity first, and then shared_ptr controls the allocation of memory blocks. Using make_shared can combine the two steps:
int main() { { std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>(); } }
shared_ptr can be copied.
int main() { { std::shared_ptr<Entity> entity = std::make_shared<Entity>(); std::shared_ptr<Entity> e0 = sharedEntity; } }
Let's change the main function and create two scopes to demonstrate:
int main() { {//Scope 1 std::shared_ptr<Entity> e0; {//Scope 2 std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>(); e0 = sharedEntity; } // At this time, sharedent is dead, but e0 is still alive (reference count is 1), so the destructor is not called here } //Here e0 also dies (the reference count is 0), and the destructor is called. }
In one-step debugging, when entering scope 1, the Created Entity is output. At this time, scope 2 is out, and the Entity is not destructed, because e0 still exists and holds a reference to the Entity. Out of scope 1, output Destroyed Entity, and all references disappear at this time.
Weak pointer_ ptr
A shared_ptr is assigned to another shared_ptr will increase the reference count, but a shared_ptr is assigned to a weak_ptr does not increase the reference count. This is often used: if you don't want the ownership of entities, for example, if you are sorting a list of entities, you don't care whether they are valid. You only need to store a reference to them, then you can use weak_ptr. You can ask weak_ptr whether the underlying object is still alive, but it will not keep the underlying object alive because it will not increase the reference count.
int main() { {//Scope 1 std::weak_ptr<Entity> e0; {//Scope 2 std::shared_ptr<Entity> sharedEntity = std::make_shared<Entity>(); e0 = sharedEntity; // Weak pointers do not reference counts }//The destructor is called when scope 2 is out //At this point, break_ Invalid PTR pointer } }
To sum up, using smart pointers can automate memory management and prevent memory leakage caused by forgetting to call delete. Unique is preferred_ PTR. If you need to copy and share between scopes, use shared_ptr.