Smart Pointer - C++

1. Why do you need smart pointers?

Let's first analyze whether the following program has any memory problems?
A reminder: pay attention to analyzing the problems in the MergeSort function:

int div()
{
    
    
    int a, b;
    cin >> a >> b;
    if (b == 0)
        throw invalid_argument("除0错误");
    return a / b;
}
void Func()
{
    
    
    // 1、如果p1这里new 抛异常会如何?
    // 2、如果p2这里new 抛异常会如何?
    // 3、如果div调用这里又会抛异常会如何?
    int* p1 = new int;
    int* p2 = new int;
    cout << div() << endl;
    delete p1;
    delete p2;
 cout << "释放资源" << endl;
}
int main()
{
    
    
    try
    {
    
    
        Func();
    }
    catch (exception& e)
    {
    
    
        cout << e.what() << endl;
    }
    return 0;
}
  1. Generally speaking, there is no problem with new and delete, but if an exception is thrown here, the call will be crossed-no release.
    Jump directly from throw to catch catch
  2. Bigger problem: when multiple consecutive exceptions need to be thrown together, some resources in front need to be released
    insert image description here
    insert image description here

As shown in the figure, there is no release at this time

 

2. Memory leak

2.1 What is a memory leak and the harm of a memory leak

什么是内存泄漏:
Memory leak refers to the situation where the program fails to release the memory that is no longer used due to negligence or error. A memory leak does not refer to the physical disappearance of memory, but rather the loss of control over a certain segment of memory due to a design error after the application allocates a certain segment of memory, resulting in a waste of memory.

内存泄漏的危害:
Memory leaks occur in long-running programs, which have a great impact, such as operating systems, background services, etc. Memory leaks will lead to slower and slower responses, and eventually freeze.

void MemoryLeaks()
{
    
    
// 1.内存申请了忘记释放
int* p1 = (int*)malloc(sizeof(int));
int* p2 = new int;
// 2.异常安全问题
int* p3 = new int[10];
Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
delete[] p3;
}

内存泄漏是指针丢了还是内存丢了?
The pointer is lost! If the pointer is still there, the memory can still be freed. The memory is still there, and the memory will be released after the process ends normally
1. Zombie processes have memory leaks
2. Long-running programs

 

2.2 Classification of memory leaks (understanding)

In C/C++ programs, we generally care about two aspects of memory leaks:

  • Heap memory leak (Heap leak)
    Heap memory refers to a piece of memory allocated from the heap through malloc / calloc / realloc / new, etc. during program execution as needed, and must be deleted by calling the corresponding free or delete after use. Assuming that the design error of the program causes this part of the memory to not be released, then this part of the space will no longer be used in the future, and Heap Leak will occur.
  • System resource leakage
    refers to the resources allocated by the system used by the program, such as sockets, file descriptors, pipes, etc., which are not released using the corresponding functions, resulting in waste of system resources, which can seriously reduce system performance and cause unstable system execution.

 

2.3 How to avoid memory leaks

  1. Good design specifications in the early stage of the project, develop good coding standards, and remember to release the memory space that matches. ps: This ideal state. But if you encounter an exception, even if you pay attention to release, there may still be problems. It needs to be managed by the next smart pointer to be guaranteed.
  2. Use RAII ideas or smart pointers to manage resources
  3. Some company internal specifications use internally implemented private memory management libraries. This library comes with options for memory leak detection.
  4. Something went wrong using a memory leak tool to detect. ps: However, many tools are not reliable enough, or the fees are expensive.

检测工具内部原理:
The application memory is recorded in a container
. When the memory is released, it is deleted from the container.
Before the end of the program or when there is no task running, the resources in the container may be memory leaks

总结一下:
Memory leaks are very common, and there are two solutions:
1. Pre-prevention type. Such as smart pointers, etc.
2. Post-event error checking type. Such as leak detection tools.

 

 

3. The use and principle of smart pointers

3.1 RAII

——获取资源就马上初始化

RAII (Resource Acquisition Is Initialization) is a simple technique that uses the object life cycle to control program resources (such as memory, file handles, network connections, mutexes, etc.).
Acquire resources when the object is constructed, then control access to the resources so that they remain valid throughout the life of the object, and finally release the resources when the object is destructed. In this way, we actually entrust the responsibility of managing a resource to an object.

This approach has two major benefits:
1. There is no need to explicitly release resources.
2. In this way, the resources required by the object are always kept valid during its lifetime to utilize the lifecycle of other classes

Use smart pointers to solve the previous memory leak problem:

//利用RAII思想设计delete资源的类
template<class T>
class SmartPtr
{
    
    
public:
    SmartPtr(T* ptr)
        :_ptr(ptr)
    {
    
    }


       ~SmartPtr()
       {
    
    
           cout << "delete:" << _ptr << endl;
           delete _ptr;
       }
private:
    T* _ptr;
};

int div()
{
    
    
    int a, b;
    cin >> a >> b;
    if (b == 0)
        throw invalid_argument("除0错误");
    return a / b;
}


void Func()
{
    
    
    // 1、如果p1这里new 抛异常会如何?
    // 2、如果p2这里new 抛异常会如何?
    // 3、如果div调用这里又会抛异常会如何?
    SmartPtr<int> sp1(new int);
    SmartPtr<int> sp2(new int);
    cout << div() << endl;
    
    cout << "释放资源" << endl;
}
int main()
{
    
    
    try
    {
    
    
        Func();
    }
    catch (exception& e)
    {
    
    
        cout << e.what() << endl;
    }
    return 0;
}

insert image description here

insert image description here

So it seems that both free up resources.
Here, no matter whether the normal func ends or an exception is thrown, sp1 and sp2 will call the destructor to release resources, which solves the problem very well.

 

The above is just the basic usage of smart pointers.
There are other usages:

3.2 The principle of smart pointer

The aforementioned SmartPtr cannot yet be called a smart pointer, because it does not yet have the behavior of a pointer. The pointer can be dereferenced, and you can also use -> to access the content in the pointed space. Therefore: in the AutoPtr template class, you need to overload * and -> to make it work like a pointer.

// 2、像指针一样的行为——重载运算符
template<class T>
class SmartPtr
{
    
    
public:
    SmartPtr(T* ptr)
        :_ptr(ptr)
    {
    
    }

    ~SmartPtr()
    {
    
    
        cout << "delete:" << _ptr << endl;
        delete _ptr;
    }

    T& operator*()
    {
    
    
        return *_ptr;
    }

    T* operator->()
    {
    
    
        return _ptr;
    }

private:
    T* _ptr;
};

Summarize the principles of smart pointers:
1. Utilize RAII thinking to design delete resource classes
2. Behavior like pointers - overloaded operators
3. Copy problem

 

3.3 std::auto_ptr

The smart pointer of auto_ptr is provided in the C++98 version of the library. The use and problems of auto_ptr demonstrated below. auto_ptr的实现原理: 管理权转移The idea, the following simplified simulation implements a bit::auto_ptr to understand its principle

SmartPtr<A> sp1(new A);
    sp1->_a1++;
    sp1->_a2++;

    //致命问题:拷贝问题——直接报错
SmartPtr<A> sp2(sp1);

 

sp2 needs to call the copy construction, but we did not write the copy construction, the compiler generates a default, shallow copy of the built-in type, custom type copy construction
But here is only one built-in type:

private:
	T* _ptr;

At this time, the shallow copy points to the same space, and the destructor will be destructed twice. How to solve?
Solution:
deep copy solution? No, it violates the functional requirements - what is needed is a shallow copy

 

This situation is very similar to iterators, but why is there no problem with shallow copies of iterators?

list<int> lt;
auto it = lt.begin();

The iterator does not manage resource release, but only accesses, traverses, and modifies data in a unified way. Release is handled by the destructor of the linked list.

Curry's copy is no problem:

auto_ptr<A> ap1(new A);//出了作用域自己会调析构函数
    ap1->_a1++;
    ap1->_a2++;

auto_ptr<A> ap2(ap1);

How did you solve it?
管理权转移——不负责的拷贝

The real problem with auto_ptr:

auto_ptr<A> ap1(new A);//出了作用域自己会调析构函数
    ap1->_a1++;
    ap1->_a2++;

auto_ptr<A> ap2(ap1);
        ap1->_a1++;
    ap1->_a2++;
        ap2->_a1++;
    ap2->_a2++;

It is guaranteed to be released when not accessing ap1:
insert image description here

But if you dereference it again, a null pointer problem will occur, and the copied object will be left empty.

insert image description here

 

class A
{
    
    
public:
    ~A()
    {
    
    
        cout << "~A()" << endl;
    }
    //private:
    int _a1 = 0;
    int _a2 = 0;
};

template<class T>
class SmartPtr {
    
    
public:
    SmartPtr(T* ptr = nullptr)
        : _ptr(ptr)
    {
    
    }

    ~SmartPtr()
    {
    
    
        if (_ptr)
        {
    
    
            cout << "Delete:" << _ptr << endl;
            delete _ptr;
        }
    }

    T& operator*()
    {
    
    
        return *_ptr;
    }

    T* operator->()
    {
    
    
        return _ptr;
    }

private:
    T* _ptr;
};

namespace haha
{
    
    
    // C++98  auto_ptr 管理权转移,被拷贝对象的出现悬空问题
    // 很多公司是明确的要求了不能使用它
    template<class T>
    class auto_ptr
    {
    
    
    public:
        auto_ptr(T* ptr = nullptr)
            : _ptr(ptr)
        {
    
    }

        auto_ptr(auto_ptr<T>& ap)
            :_ptr(ap._ptr)
        {
    
    
            ap._ptr = nullptr;
        }

        // ap1 = ap2;
        auto_ptr<T>& operator=(auto_ptr<T>& ap)
        {
    
    
            if (this != &ap)
            {
    
    
                if (_ptr)
                {
    
    
                    cout << "Delete:" << _ptr << endl;
                    delete _ptr;
                }

                _ptr = ap._ptr;
                ap._ptr = nullptr;
            }

            return *this;
        }

        ~auto_ptr()
        {
    
    
            if (_ptr)
            {
    
    
                cout << "Delete:" << _ptr << endl;
                delete _ptr;
            }
        }

        T& operator*()
        {
    
    
            return *_ptr;
        }

        T* operator->()
        {
    
    
            return _ptr;
        }

    private:
        T* _ptr;
    };

void test_auto_ptr()
{
    
    
    std::auto_ptr<A> ap1(new A);
    ap1->_a1++;
    ap1->_a2++;

    std::auto_ptr<A> ap2(ap1);
    ap1->_a1++;
    ap1->_a2++;
    ap2->_a1++;
    ap2->_a2++;

    // 2 2
    cout << ap2->_a1 << endl;
    cout << ap2->_a2 << endl;

    std::auto_ptr<A> ap3(new A);
    ap2 = ap3;

    ap2->_a1++;
    ap2->_a2++;

    cout << ap2->_a1 << endl;
    cout << ap2->_a2 << endl;
}

insert image description here
 

namespace haha
{
    
    
    
    template<class T>
    class auto_ptr
    {
    
    
    public:
        auto_ptr(T* ptr = nullptr)
            : _ptr(ptr)
        {
    
    }

        auto_ptr(auto_ptr<T>& ap)
            :_ptr(ap._ptr)
        {
    
    
            ap._ptr = nullptr;
        }

        // ap1 = ap2;
        auto_ptr<T>& operator=(auto_ptr<T>& ap)
        {
    
    
            if (this != &ap)
            {
    
    
                if (_ptr)
                {
    
    
                    cout << "Delete:" << _ptr << endl;
                    delete _ptr;
                }

                _ptr = ap._ptr;
                ap._ptr = nullptr;
            }

            return *this;
        }

        ~auto_ptr()
        {
    
    
            if (_ptr)
            {
    
    
                cout << "Delete:" << _ptr << endl;
                delete _ptr;
            }
        }

        T& operator*()
        {
    
    
            return *_ptr;
        }

        T* operator->()
        {
    
    
            return _ptr;
        }

    private:
        T* _ptr;
    };
}

void test_auto_ptr()
{
    
    
    haha::auto_ptr<A> ap1(new A);
    ap1->_a1++;
    ap1->_a2++;

    haha::auto_ptr<A> ap2(ap1);
    //ap1->_a1++;
    //ap1->_a2++;
    ap2->_a1++;
    ap2->_a2++;

    // 2 2
    cout << ap2->_a1 << endl;
    cout << ap2->_a2 << endl;

    haha::auto_ptr<A> ap3(new A);
    ap2 = ap3;

    ap2->_a1++;
    ap2->_a2++;

    cout << ap2->_a1 << endl;
    cout << ap2->_a2 << endl;
}

insert image description here

 
 

3.4 std::unique_ptr

Unique pointer
Design ideas: simple and rude, no copying

    // C++11库才更新智能指针实现
// C++11出来之前,boost搞除了更好用的scoped_ptr/shared_ptr/weak_ptr
// C++11将boost库中智能指针精华部分吸收了过来
// C++11->unique_ptr/shared_ptr/weak_ptr
// unique_ptr/scoped_ptr
// 原理:简单粗暴 -- 防拷贝
 template<class T>
    class unique_ptr
    {
    
    
    private:
        // 防拷贝 C++98
        // 只声明不实现 —— 声明成私有
        //unique_ptr(unique_ptr<T>& ap);
        //unique_ptr<T>& operator=(unique_ptr<T>& ap);
    public:
        unique_ptr(T* ptr = nullptr)
            : _ptr(ptr)
        {
    
    }

        // 防拷贝 C++11
        unique_ptr(unique_ptr<T>& ap) = delete;
        unique_ptr<T>& operator=(unique_ptr<T>& ap) = delete;

        ~unique_ptr()
        {
    
    
            if (_ptr)
            {
    
    
                cout << "Delete:" << _ptr << endl;
                delete _ptr;
            }
        }

        T& operator*()
        {
    
    
            return *_ptr;
        }

        T* operator->()
        {
    
    
            return _ptr;
        }

    private:
        T* _ptr;
    };
}
    
// unique_ptr:简单粗暴,不让拷贝 只适用于不需要拷贝一些场景
void test_unique_ptr()
{
    
    
    haha::unique_ptr<A> up1(new A);
    //bit::unique_ptr<A> up2(up1);
    up1->_a1++;
    up1->_a2++;

    haha::unique_ptr<A> up3(new A);
    //up1 = up2;
}

insert image description here

It is
only suitable for some scenarios that do not need to be copied

 
 

3.5 std::shared_ptr

C++11 began to provide a more reliable shared_ptr that supports copying

The principle of shared_ptr is to share resources between multiple shared_ptr objects by means of reference counting.

  1. shared_ptr maintains a count for each resource inside it, which is used to record that the resource is shared by several objects.
  2. When the object is destroyed (that is, the destructor is called), it means that the resource is no longer used, and the reference count of the object is reduced by one.
  3. If the reference count is 0, it means that it is the last object to use the resource and must release the resource;
  4. If it is not 0, it means that there are other objects using this resource besides yourself, and the resource cannot be released, otherwise other objects will become wild pointers.

The following are the scenarios that need to be copied

// shared_ptr 需要拷贝的场景
void test_shared_ptr1()
{
    
    
    std::shared_ptr<A> sp1(new A);
    std::shared_ptr<A> sp2(sp1);
    std::shared_ptr<A> sp3(sp1);

    sp1->_a1++;
    sp1->_a2++;
    cout << sp2->_a1 << ":" << sp2->_a2 << endl;
    sp2->_a1++;
    sp2->_a2++;
    cout << sp1->_a1 << ":" << sp1->_a2 << endl;

    std::shared_ptr<A> sp5(new A);
    std::shared_ptr<A> sp6(sp5);

    sp1 = sp5;
    sp2 = sp5;
    sp3 = sp5;

    // 自己给自己赋值
    std::shared_ptr<int> sp4(new int);
    sp4 = sp4;
    sp1 = sp5;
}

insert image description here

The feasible idea of ​​co-management is:
when each object is released, – count
the last destructed object and release resources

 
 

//这种写法是不可行的
    //template<class T>
    //class shared_ptr
    //{
    
    
    //public:
    //    shared_ptr(T* ptr = nullptr)
    //        : _ptr(ptr)
    //    {
    
    
    //        ++_count;
    //    }

    //    ~shared_ptr()
    //    {
    
    
    //        if (--_count == 0 && _ptr)
    //        {
    
    
    //            cout << "Delete:" << _ptr << endl;
    //            delete _ptr;
    //        }
    //    }

    //    shared_ptr(shared_ptr<T>& sp)
    //        : _ptr(sp._ptr)
    //    {
    
    
    //        ++_count;
    //    }

    //    T& operator*()
    //    {
    
    
    //        return *_ptr;
    //    }

    //    T* operator->()
    //    {
    
    
    //        return _ptr;
    //    }

    //private:
    //    T* _ptr;

    //    static int _count; // 引用计数
    //};

    //template<class T>
    //int shared_ptr<T>::_count = 0;
template<class T>
    class shared_ptr
    {
    
    
    public:
        shared_ptr(T* ptr = nullptr)
            : _ptr(ptr)
            , _pCount(new int(1))
        {
    
    }

        void Release()
        {
    
    
            if (--(*_pCount) == 0)
            {
    
    
                cout << "Delete:" << _ptr << endl;
                delete _ptr;
                delete _pCount;
            }
        }

        ~shared_ptr()
        {
    
    
            Release();
        }

        // sp1(sp2)
        shared_ptr(const shared_ptr<T>& sp)
            : _ptr(sp._ptr)
            , _pCount(sp._pCount)
        {
    
    
            (*_pCount)++;
        }

        // sp1 = sp5
        // sp1 = sp1
        
        shared_ptr<T>& operator=(const shared_ptr<T>& sp)
        {
    
    
            //if (this == &sp)
            if (_ptr == sp._ptr)
            {
    
    
                return *this;
            }

            // 减减被赋值对象的计数,如果是最后一个对象,要释放资源
            /*if (--(*_pCount) == 0)
            {
            delete _ptr;
            delete _pCount;
            }*/
            Release();

            // 共管新资源,++计数
            _ptr = sp._ptr;
            _pCount = sp._pCount;

            (*_pCount)++;

            return *this;
        }

        T& operator*()
        {
    
    
            return *_ptr;
        }

        T* operator->()
        {
    
    
            return _ptr;
        }

    private:
        T* _ptr;

        // 引用计数
        int* _pCount;
    };
 
 
 // shared_ptr 需要拷贝的场景
void test_shared_ptr1()
{
    
    
    haha::shared_ptr<A> sp1(new A);
    haha::shared_ptr<A> sp2(sp1);
    haha::shared_ptr<A> sp3(sp1);

    sp1->_a1++;
    sp1->_a2++;
    cout << sp2->_a1 << ":" << sp2->_a2 << endl;
    sp2->_a1++;
    sp2->_a2++;
    cout << sp1->_a1 << ":" << sp1->_a2 << endl;

    haha::shared_ptr<A> sp5(new A);
    haha::shared_ptr<A> sp6(sp5);

    sp1 = sp5;
    sp2 = sp5;
    sp3 = sp5;

    // 自己给自己赋值
    haha::shared_ptr<int> sp4(new int);
    sp4 = sp4;
    sp1 = sp5;
}

insert image description here

Static counting objects are not allowed.
One resource is allocated with one count. Multiple smart pointer objects manage static counting objects. All resources have only one count, because static members belong to the entire class, and all objects belonging to the class need to be managed when each resource needs to be managed
. To the constructor, construct new a count.

3.5.1 Circular reference analysis:

  1. The two smart pointer objects node1 and node2 point to two nodes, the reference count becomes 1, and we don't need to manually
    delete.
  2. The _next of node1 points to node2, the _prev of node2 points to node1, and the reference count becomes 2.
  3. Node1 and node2 are destroyed, and the reference count is reduced to 1, but _next still points to the next node. But _prev still points to
    the previous node.
  4. That is to say, _next is destructed, and node2 is released.
  5. That is to say, _prev is destructed, and node1 is released.
  6. But _next is a member of node, node1 is released, _next will be destructed, and node1 is managed by _prev, _prev is a member of node2, so this is called a circular reference, and no one will release it

循环引用:

struct Node
{
    
    
    int _val;
    std::shared_ptr<Node> _next;
    std::shared_ptr<Node> _prev;



    ~Node()
    {
    
    
        cout << "~Node" << endl;
    }
};


void test_shared_ptr2()
{
    
    
    std::shared_ptr<Node> n1(new Node);
    std::shared_ptr<Node> n2(new Node);

    n1->_next = n2;//不能赋值过去,自定义对象不能赋值给原生指针
    n2->_prev = n1;

    
}

After this function ends, n2 will be destructed first, and then n1 will be destructed.
_next manages the node memory block on the right
_prev manages the node memory block on the left

_next is destructed, the right node is deleted, when will _next be released? The
left node is deleted, the destructor is called, and _next will be destructed as a member

_prev is destructed, the left node is deleted, when will _prev be released? The
right node is deleted, the destructor is called, and _prev will be destructed as a member

 
 

解决方法:

struct Node
{
    
    
    int _val;    

std::weak_ptr<Node> _next;
std::weak_ptr<Node> _prev;

    ~Node()
    {
    
    
        cout << "~Node" << endl;
    }
};

// 循环引用 -- weak_ptr不是常规智能指针,没有RAII,不支持直接管理资源
// weak_ptr主要用shared_ptr构造,用来解决shared_ptr循环引用问题
void test_shared_ptr2()
{
    
    
    

    std::shared_ptr<Node> n1(new Node);//不支持隐式类型转换
    std::shared_ptr<Node> n2(new Node);

    cout << n1.use_count() << endl;
    cout << n2.use_count() << endl;

    n1->_next = n2;
    n2->_prev = n1;

    cout << n1.use_count() << endl;
    cout << n2.use_count() << endl;
}

insert image description here

特点:
When _next and _prev are weak_ptr, he does not participate in resource release management, can access and modify resources, but does not increase the count, so there is no problem of circular references.

 

模拟实现weak_ptr

   //辅助型智能指针,配合解决shared_ptr的循环引用问题
    template<class T>
    class weak_ptr
    {
    
    
    public:
        weak_ptr()
            :_ptr(nullptr)
        {
    
    }

        weak_ptr(const shared_ptr<T>& sp)
            :_ptr(sp.get())
        {
    
    }

        weak_ptr(const weak_ptr<T>& wp)
            :_ptr(wp._ptr)
        {
    
    }

        weak_ptr<T>& operator=(const shared_ptr<T>& sp)
        {
    
    
            _ptr = sp.get();
            return *this;
        }

        T& operator*()
        {
    
    
            return *_ptr;
        }

        T* operator->()
        {
    
    
            return _ptr;
        }
    public:
        T* _ptr;
    };

 
 

If it is not a new object, how can it be managed by a smart pointer? In fact, shared_ptr designed a deleter to solve this problem.
The new[] built-in type has no problem, and the custom type will report an error.

template<class T>
struct DeleteArray
{
    
    
    void operator()(T* ptr)
    {
    
    
        cout << "delete[]" << ptr << endl;
        delete[] ptr;
    }
};

template<class T>
struct Free
{
    
    
    void operator()(T* ptr)
    {
    
    
        cout << "free" << ptr << endl;
        free(ptr);
    }
};

// 定制删除器
//void test_shared_ptr3()
//{
    
    
//    // 仿函数对象
//    /*std::shared_ptr<Node> n1(new Node[5], DeleteArray<Node>());
//    std::shared_ptr<Node> n2(new Node);
//
//    std::shared_ptr<int> n3(new int[5], DeleteArray<int>());
//
//    std::shared_ptr<int> n4((int*)malloc(sizeof(12)), Free<int>());*/
//
//    // lambda
//    //std::shared_ptr<Node> n1(new Node[5], [](Node* ptr){delete[] ptr; });
//    //std::shared_ptr<Node> n2(new Node);
//
//    //std::shared_ptr<int> n3(new int[5], [](int* ptr){delete[] ptr; });
//
//    //std::shared_ptr<int> n4((int*)malloc(sizeof(12)), [](int* ptr){free(ptr); });
//    //std::shared_ptr<FILE> n5(fopen("test.txt", "w"), [](FILE* ptr){fclose(ptr); });
//
//    //std::unique_ptr<Node, DeleteArray<Node>> up(new Node[5]);
//}

void test_shared_ptr3()
{
    
    
    haha::shared_ptr<Node, DeleteArray<Node>> n1(new Node[5]);
    haha::shared_ptr<Node> n2(new Node);
    haha::shared_ptr<int, DeleteArray<int>> n3(new int[5]);
    haha::shared_ptr<int, Free<int>> n4((int*)malloc(sizeof(12)));
}

 
 

4. The relationship between smart pointers in C++11 and boost

  1. The first smart pointer auto_ptr was created in C++98.
  2. C++ boost gives more practical scoped_ptr and shared_ptr and weak_ptr.
  3. C++ TR1, introduced shared_ptr, etc. But note that TR1 is not a standard version.
  4. C++11, introduced unique_ptr and shared_ptr and weak_ptr. It should be noted that unique_ptr corresponds to boost's scoped_ptr. And the implementation principles of these smart pointers refer to the implementation in boost.

Guess you like

Origin blog.csdn.net/Ll_R_lL/article/details/128905227