Detailed explanation of C++ smart pointer shared_ptr

1. Introduction

The C++ smart pointer shared_ptr is a smart pointer that can automatically manage memory. It is one of the new features of C++11. Different from traditional pointers, shared_ptr can automatically release the managed memory of dynamically allocated objects, and avoid the cumbersome operations of manually releasing memory, thereby reducing the occurrence of memory leaks and wild pointers.

shared_ptr is a template class that implements multiple smart pointers to share ownership of an object through a reference counter. Every time a shared_ptr object is copied, the object's reference counter is incremented by 1, and when the last shared_ptr object is destroyed, the reference counter is decremented by 1, and if the reference counter becomes 0, the managed object's memory is released.

Using shared_ptr requires including a header file, and can be created by:

std::shared_ptr<int> p(new int(10));

The above code creates a shared_ptr object p, which points to a dynamically allocated int type object with an initial value of 10.

When using shared_ptr, you need to pay attention to the following points:

  1. Do not use raw pointers to initialize shared_ptr, otherwise the same object may be deleted multiple times.

  2. Avoid storing arrays in shared_ptr, because shared_ptr can only handle the release of a single object, not the destruction of an array properly.

  3. Objects can be released in a specific way through a custom deleter.

  4. shared_ptr can be passed as a function parameter, but be careful to avoid the problem of circular reference, otherwise it will lead to memory leaks.

shared_ptr is a convenient and safe memory management tool that can effectively avoid memory leaks and wild pointers.

Second, the underlying principle

insert image description here

2.1, reference counting

The core of shared_ptr is the reference counting technology. In each shared_ptr object, there is a pointer to the managed object and an integer counter. This counter counts how many shared_ptr objects point to the managed object. When a new shared_ptr object points to the same memory, the memory's reference count is increased by 1. When a shared_ptr object no longer points to the memory, the reference count of the memory is decremented by 1. When the reference count is 0, it means that no shared_ptr object points to the memory, and the memory will be released automatically.

2.2. Construction and destruction of shared_ptr

  1. The constructor of shared_ptr takes a pointer as a parameter, which points to the object to be managed. When a new shared_ptr object is created, it attempts to increment the reference count of the managed object. If the object is not already managed by another shared_ptr object, a new reference count is created and set to 1. Otherwise, it shares the same reference count with the existing shared_ptr object.

  2. The shared_ptr's destructor attempts to decrement the reference count of the managed object. If the reference count becomes 0, the managed object's memory is automatically freed.

  3. The control block of shared_ptr (including information such as reference count and deleter) will be released when the last shared_ptr pointing to the managed object is destructed. When the reference count is reduced to 0, it means that there is no shared_ptr object pointing to the managed object. At this time, shared_ptr will automatically call the deleter and release the control block. Since shared_ptr can share the same control block, the control block can only be released after all shared_ptr objects are destroyed. If a shared_ptr object is manually disassociated from the managed object using the reset() method, the reference count will be reduced accordingly. When the reference count becomes 0, the control block will also be released.

2.3, sharing and copying of shared_ptr

A shared_ptr can share the same pointer to an object with other shared_ptr objects. When a shared_ptr object is copied, the reference count of the object it manages is also increased by 1. Therefore, any shared_ptr object holding the same pointer can affect all other shared_ptr objects by changing the state of the object it manages.

2.4, circular reference problem

shared_ptr is very effective in dealing with circular reference problems. For example, if an object A contains a shared_ptr pointing to another object B, and object B also contains a shared_ptr pointing to object A, then the two objects will form a circular reference. In this case, memory leaks may occur if plain pointers are used. However, since shared_ptr uses reference counting techniques, both objects will be properly deallocated when no other shared_ptr objects refer to them.

Three, the use of shared_ptr

The syntax for creating shared_ptran object is as follows:

  1. Created by the new keyword
std::shared_ptr<int> p(new int);
  1. Created by the make_shared function, which avoids the use of the new keyword
std::shared_ptr<int> p = std::make_shared<int>();
  1. pass pointer and deleter as arguments to create
void my_deleter(int* p) {
    
    
    delete p;
}

std::shared_ptr<int> p(new int, my_deleter);
  1. pass pointer, deleter and allocator as arguments to create
void my_deleter(int* p) {
    
    
    delete p;
}

struct MyAllocator {
    
    
    void* allocate(size_t size);
    void deallocate(void* ptr, size_t size);
};

MyAllocator my_allocator;

std::shared_ptr<int> p(new int, my_deleter, my_allocator);
  1. shared_ptrcreated from another object
std::shared_ptr<int> p1(new int);
std::shared_ptr<int> p2(p1);
  1. shared_ptrCreated from another object using move semantics
std::shared_ptr<int> p1(new int);
std::shared_ptr<int> p2(std::move(p1));

3.1. Create a shared_ptr

Creating a smart pointer using shared_ptr is very simple, just pass a raw pointer to dynamically allocated memory as a parameter to the constructor of shared_ptr:

// 创建一个int类型的智能指针
std::shared_ptr<int> p(new int(10));

3.2, share a shared_ptr

shared_ptr can share the same pointer to the object with other shared_ptr objects, so that the problem of multiple dynamic memory allocation and release of memory can be avoided. Sharing a shared_ptr can be achieved with copy constructor and assignment operator:

// 复制构造函数
std::shared_ptr<int> p1(new int(10));
std::shared_ptr<int> p2(p1);

// 赋值运算符
std::shared_ptr<int> p3(new int(10));
std::shared_ptr<int> p4;
p4 = p3;

Note: Sharing a shared_ptr increases the reference count of the managed object. Therefore, any shared_ptr object holding the same pointer can affect all other shared_ptr objects by changing the state of the object it manages.

3.3. Using deleter

In addition to managing the allocated memory, shared_ptr can also use the deleter (deleter) to manage objects. A deleter is a function or function object used to perform specific operations when shared_ptr releases the managed object. The deleter can be specified via a shared_ptr template parameter:

// 使用Lambda表达式作为删除器
std::shared_ptr<int> p(new int(10), [](int* p){
    
     delete[] p; });

3.4. Disassociation

If you need to disassociate the shared_ptr from the managed object, you can use the reset() method:

std::shared_ptr<int> p(new int(10));
p.reset();

Note: When the reset() method is called, the reference count of the managed object will be reduced by 1. If the reference count becomes 0, the managed object's memory is automatically freed.

4. Example of use

#include <memory>
#include <iostream>

using namespace std;

class MyClass {
    
    
public:
    MyClass() {
    
     cout << "MyClass constructor" << endl; }
    ~MyClass() {
    
     cout << "MyClass destructor" << endl; }
    void printInfo() {
    
     cout << "This is MyClass" << endl; }
};

int main() {
    
    
    shared_ptr<MyClass> p1(new MyClass()); // 创建一个shared_ptr指向MyClass对象
    shared_ptr<MyClass> p2 = p1; // p1和p2都指向同一个MyClass对象

    p1->printInfo(); // 访问MyClass对象的成员函数
    p2.reset(); // 释放p2所指向的MyClass对象
    p1->printInfo(); // 由于p1仍然指向MyClass对象,所以此处输出"This is MyClass"

    return 0;
}

In the above code, shared_ptr<MyClass>two pointers p1 and p2 are created by calling the constructor, and they both point to an MyClassobject. We call reset()the function to free the object pointed to by p2 MyClass, but since p1 still points to that object, it p1->printInfo()still prints "This is MyClass" when called. When the program ends, MyClassthe object pointed to by p1 will be automatically released.

As you can see, using it shared_ptrcan easily avoid problems such as memory leaks and dangling pointers. In addition, it should be noted that shared_ptrthe assignment and copy operations between pointers will increase the reference count of the pointed object. Even if a pointer has released the object it points to, as long as other pointers are still using the object, the object will not be used. Automatically deleted. Therefore, shared_ptryou need to pay attention to the life cycle of the object when using it to avoid unexpected side effects.

Summarize

Smart pointer is an important language mechanism in C++, among which shared_ptr is one of the most commonly used and classic smart pointers.

  1. shared_ptr is a reference-counted smart pointer that can share the same object.

  2. When using shared_ptr, the header file <memory> needs to be included.

  3. When creating a shared_ptr object, you can directly pass the raw pointer as a parameter to the constructor, or use the make_shared function to create it.

  4. The reference count of the object is automatically updated when the shared_ptr object is initialized, copied, and released.

  5. When a shared_ptr object is destroyed, the reference count of the object it points to will decrease. If the reference count is 0, the object will be automatically deleted.

  6. The raw pointer managed by the shared_ptr object can be obtained through the get function.

  7. The original pointer managed by the shared_ptr object can be rebound through the reset function.

  8. You can use the unique function to determine whether the shared_ptr object uniquely owns the original pointer.

  9. Normally, shared_ptr objects should be created on the stack, not on the heap using the new operator.

  10. When using shared_ptr in a multi-threaded environment, you need to pay attention to thread safety measures, such as using locks to ensure the correctness of reference counting.

  11. shared_ptr is part of the STL in C++11. It is a template class used to manage memory for dynamically allocated objects. shared_ptr can automatically complete memory management, ensure that memory is released correctly, and is very easy to use.

  12. shared_ptr is a powerful smart pointer class that utilizes reference counting techniques to manage the memory of dynamically allocated objects. shared_ptr can avoid problems such as circular references and memory leaks, and is easy to use. It is one of the essential tools for C++ programmers.

insert image description here

Guess you like

Origin blog.csdn.net/Long_xu/article/details/130294737