C++--deep understanding of smart pointers

PS: See here for the simple application of smart pointers  http://t.csdn.cn/qN7IK

1. Introduction to smart pointers

In C++, there are three versions of smart pointers, namely:

auto_ptr  unique_ptr shared_ptr 

Among the three versions of smart pointers, shared_ptr is the most perfect, auto_ptr is basically useless, and unique_ptr is not used in many scenarios. For smart pointers, you need to implement RAII and overload operator* and operator-> to make it behave like a pointer.

2. Smart pointer analysis

    2.1 auto_ptr version

        Auto_ptr simple implementation:

    template<class T>
	class Auto_ptr
	{
	public:
        //管理权转移 auto_ptr
		Auto_ptr(T* ptr)
			:_ptr(ptr)
		{}

		Auto_ptr(Auto_ptr<T>& ptr)
			:_ptr(ptr._ptr)
		{
			ptr._ptr=nullptr;
		}

		T& operator=(const T& ptr)
		{
			if (this != &ptr)
			{
				if (_ptr)
				{
					cout << "Delete:" << _ptr << endl;
					delete _ptr;
				}

				_ptr = ptr._ptr;
				ptr._ptr = nullptr;
			}

			return *this;

		}
        //析构函数需要写重载,不同常见需要不同析构函数
		/*~Auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}*/
		T& operator*()
		{
			return *this;
		}

		T* operator->()
		{
			return this;
		}
	private:
		T* _ptr=nullptr;
	};

Since auto_ptr supports copy construction, in order to ensure that the pointer owner is unique, the ownership is transferred here. After the transfer of ownership, the copied one is empty. This is a major disadvantage, causing auto_ptr to be abandoned.

2.2. Use of unique_ptr   

template<class T>
	class Unique_ptr
	{
	public:
		Unique_ptr(T* ptr)
			:_ptr(ptr)
		{}

		Unique_ptr(Auto_ptr<T>& ptr) = delete;
		
		T& operator=(const T& ptr) = delete;
		/*~Auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}*/
		T& operator*()
		{
			return *this;
		}

		T* operator->()
		{
			return this;
		}
	private:
		T* _ptr = nullptr;
	};

    The underlying layer of unique_ptr does not directly use copy construction and assignment overloading, so it can only be used in scenarios that do not require copy construction and copy overloading, so the disadvantage of unique_ptr is that unique_ptr does not share its pointer. It cannot be copied to another unique_ptr, cannot be passed by value to a function, and cannot be used in any Standard Template Library (STL) algorithm that requires a copy. Only unique_ptr can be moved. This means, the memory resource ownership is transferred to another unique_ptr and the original unique_ptr no longer owns this resource.

2.3 Use of shared_ptr

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();
		}

		Shared_ptr(Smart_ptr<T>& ptr)
			:_ptr(ptr)
		{
			(*_Pcount)++;
			ptr = nullptr;
		}
		Shared_ptr& operator=(Smart_ptr& ptr)
		{
			if (_ptr == ptr._ptr)
				return *this;
			Release();
			--(*_Pcount);

			_ptr = ptr._ptr;
			_Pcount = ptr._Pcount;
			*_Pcount++;
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
	private:
		T* _ptr;
		int* _Pcount;
	};

On the basis of unique_ptr, a new version has been added, which is the shared_ptr version.

Basic principle: It is to record the number of times the object is referenced. When the number of references is 0, that is, when the last shared pointer pointing to the object is destroyed, the destructor of the shared pointer releases the pointed memory area.

Features: The resource it points to is shared, that is, multiple shared_ptr can point to the same resource, and the reference counting mechanism is used internally to achieve this.

Shared pointer memory: Each shared_ptr object internally points to two memory locations:

  • pointer to the object;
  • A pointer to control reference counted data.

When a new shared_ptr object is associated with a pointer, then in its constructor, increment the reference count associated with this pointer by one.

When any shared_ptr object goes out of scope, then in its destructor, it decrements the reference count of the associated pointer by one. If the reference count becomes 0, it means that no other shared_ptr objects are associated with this memory, in which case it deletes the memory using the delete function.

A shared_ptr is used like a normal pointer, you can use * and -> with a shared_ptr object, and you can compare it like any other shared_ptr object.

However, for shared_ptr, there is still a disadvantage. If it is a circular reference, then there will be bugs, and weak_ptr needs to be used at this time.

2.4 weak_ptr use

weak_ptris a weakly referenced smart pointer that can be shared_ptrused with . weak_ptrDoes not increase the reference count of the managed object, so it does not affect the lifetime of the object. A pointer to the managed object can be obtained weak_ptrthrough the member function of .lock()shared_ptr

3. Summary:

3.1 Smart pointer principle:

The idea of ​​smart pointer to solve the problem: wrap the regular pointer, when the smart pointer object expires, let its destructor release the memory of the regular pointer.

auto_ptr (the scheme of C++98, C++11 has been abandoned): Adopt the ownership mode. For a specific object, only one smart pointer can own it, so that only the destructor of the smart pointer that owns the object will delete the object. Then, let the assignment transfer ownership.

unique_ptr (replacing auto_ptr): It also adopts the ownership mode to realize the concept of exclusive ownership or strict ownership, ensuring that only one smart pointer can point to the object at the same time.

shared_ptr: Uses reference counting to implement the concept of shared ownership. Multiple smart pointers can point to the same object, and the object and its associated resources are released when the last reference is destroyed. It uses reference counting to indicate that resources are shared by several pointers. For example, on assignment, the count will be incremented by 1, and when the pointer expires, the count will be decremented by 1. delete is called only when the last pointer expires.

weak_ptr: This type of pointer is usually not used alone (no practical use), and can only be used with shared_ptr type pointers. The weak_ptr type pointer does not affect the reference count of the pointed heap memory space, and can be used to solve the circular reference problem.

3.2 Usage Scenarios

If you use new to allocate memory from the heap (free storage area) in the program, you should use delete to release it when it is no longer needed. If you forget to release it, memory leaks will occur. C++ introduced smart pointers to help automate this process. Smart pointers are class objects that behave like pointers. If the program wants to use multiple pointers to the same object, you should choose shared_ptr; if the program does not need multiple pointers to the same object, you can use unique_ptr; if you use new [] to allocate memory, you should choose unique_ptr; if the function uses new allocates memory and returns a pointer to that memory, it is a good choice to declare its return type as unique_ptr.

Guess you like

Origin blog.csdn.net/weixin_66828150/article/details/132416110