Summary of C + + intelligent pointer

The function of intelligent pointer

Dynamic memory management often has two problems:
1. Forgetting to release memory will cause memory leak;
2. If a pointer still references memory, it will be released and a pointer referencing illegal memory will be generated.
Both of these problems will lead to memory leak, so the function of smart pointer is to leak memory by way.

Smart pointer is a class template. When it goes beyond the scope of the class, the class will automatically call the destructor, which will automatically release resources.

The use and principle of intelligent pointer

1,RAII

RAII (Resource Acquisition Is Initialization) is a simple technology that uses object lifecycle to control program resources (such as memory, file handle, network connection, mutual exclusion, etc.).

When the object is constructed, the resource is acquired, and then the access to the resource is controlled to keep it effective throughout the life cycle of the object. Finally, the resource is released 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:

  • There is no need to explicitly release resources.
  • In this way, the resources required by an object remain valid throughout its lifetime

2. The principle of intelligent pointer

struct Date
{
	int m_year;
	int m_month;
	int m_day;
};

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

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

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

	~SmartPtr()
	{
		if (m_ptr)
			delete m_ptr;
	}
};

int main()
{
	SmartPtr<int> sp1(new int);
	*sp1 = 10;
	cout << *sp1 << endl;
	SmartPtr<Date> sparray(new Date);
	// It should be noted that this should be array. Operator - > () - > m_year = 2018;
	// It should have been sparray - >
	sparray->m_year = 2018;
	sparray->m_month = 1;
	sparray->m_day = 1;
	system("pause");
	return 0;
}

summary

  1. RAII characteristics
  2. Overloaded operator * and opteraor - > with pointer like behavior

Smart pointer is a class template, but it can overload operator* and opertaor->, with the same behavior as pointer.

3. Three kinds of intelligent pointer

//Header file
#include<memeory>
auto_ptr
#include<iostream>
#include<memory>
using namespace std;

class Date
{
public:
	Date()
	{
		cout << "Date()" << endl; 
	}
	~Date() { 
		cout << "~Date()" << endl; 
	}
	int m_year;
	int m_month;
	int m_day;
};
int main()
{
	auto_ptr<Date> ap(new Date);
	auto_ptr<Date> copy(ap);
	// Auto PTR problem: when an object is copied or assigned, the previous object is suspended
	ap->m_year = 2018;
	system("pause");
	return 0;
}

According to the above code, the problem is that when the object is copied or assigned, the previous object is suspended
The principle of auto PTR is: the idea of management right transfer, copy or assign is old to new, old invalid.
C++11 has been abandoned and is no longer used.

unique_ptr
int main()
{
	unique_ptr<Date> ap(new Date);
	unique_ptr<Date> copy(ap);//error
	return 0;
}

The principle of unique PTR is to prevent copying and assignment, which cannot solve substantive problems.
Anti copy mode of C++98: only declare not implement + declare private
Anti copy mode of C++11: delete

shared_ptr
int main()
{
	// shared_ptr supports the copy of smart pointer objects by reference counting
	shared_ptr<Date> sp(new Date);
	shared_ptr<Date> copy(sp);
	cout << "ref count:" << sp.use_count() << endl;
	cout << "ref count:" << copy.use_count() << endl;
	return 0;
}
The principle of shared pur

It realizes the sharing of resources among multiple shared PTR objects by reference counting.

  1. Within the shared PTR, a count is maintained for each resource to record that the resource is shared by several objects.
  2. When an object is destroyed (that is, a destructor call), it means that it does not use the resource, 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 the resource must be released;
  4. If it is not 0, it means that other objects are using the resource and cannot release it. Otherwise, other objects will become wild pointers.
Thread safety problem of shared ﹣ PTR
  1. The reference count in the smart pointer object is shared by multiple smart pointer objects. The reference count of the smart pointer in two threads is at the same time + + or -. This operation is not atomic. The reference count used to be 1, + + twice, maybe 2. In this way, the reference count is disordered. Problems that can cause resources not to be released or programs to crash. Therefore, only the reference count + + and - in the pointer need to be locked, that is to say, the operation of reference count is thread safe.
  2. The object managed by intelligent pointer is stored on the heap and accessed by two threads at the same time, which will lead to thread safety problems.
Circular reference of shared_ptr
struct ListNode
{
	int _data;
	shared_ptr<ListNode> _prev;
	shared_ptr<ListNode> _next;
	
	~ListNode()
	{ 
		cout << "~ListNode()" << endl; 
	}
};

int main()
{
	shared_ptr<ListNode> node1(new ListNode);
	shared_ptr<ListNode> node2(new ListNode);
	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;
	node1->_next = node2;
	node2->_prev = node1;
	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;
	return 0;
}

By analyzing the above codes, the following conclusions can be drawn:

  1. node1 and node2 two intelligent pointer objects point to two nodes, the reference count becomes 1, we do not need to delete manually.
  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 destructed, the reference count is reduced to 1, but "next" also points to the next node. But prev also points to the previous node.
  4. That is to say, when the next is deconstructed, node2 is released.
  5. That is to say, when prev is deconstructed, node1 is released.
  6. But "next" belongs to the member of node1. When node1 is released, then "next" will be destructed. Node1 is managed by "prev", and "prev" belongs to the member of node2, so this is called circular reference. No one will release.

**Solution to circular reference: * * in the case of reference counting, change the "prev" and "next" in the node to "peak" PTR ".
Implementation principle: when node1 - > [next = node2; and node2 - >.

weak_ptr

Week_ptr is a kind of intelligent pointer that does not control the lifetime of the object. It is managed by a shared_ptr object. Binding a weakapuptr to a shared apuptr does not change the reference count of the shared apuptr. Once the last shared ﹐ PTR pointing to the object is destroyed, the object is released. Even if there is a "weak" PTR pointing to the object, the object will be released. Therefore, the name of "weak" PTR captures the characteristics of this intelligent pointer "weak" shared object.

When you create a weakapuptr, you need to initialize it with a shared apuptr;

auto p = make_shared<int>(42);
weak_ptr<int> wp(p); //wp weak share p, the reference count of p will not change

Solve the problem of circular reference caused by shared aputr with weak aputr

struct ListNode
{
	int _data;
	weak_ptr<ListNode> _prev;
	weak_ptr<ListNode> _next;
	
	~ListNode()
	{ 
		cout << "~ListNode()" << endl; 
	}
};

int main()
{
	shared_ptr<ListNode> node1(new ListNode);
	shared_ptr<ListNode> node2(new ListNode);
	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;
	node1->_next = node2;
	node2->_prev = node1;
	cout << node1.use_count() << endl;
	cout << node2.use_count() << endl;
	return 0;
}

If it's not the new object, we can design a remover by using shared PTR

// The remover of imitative function
//Header file of functor: include < functional >
template<class T>
struct FreeFunc
{
	void operator()(T* ptr)
	{
		cout << "free:" << ptr << endl;
		free(ptr);
	}
};
template<class T>
struct DeleteArrayFunc 
{
	void operator()(T* ptr)
	{
		cout << "delete[]" << ptr << endl;
		delete[] ptr;
	}
};

int main()
{
	FreeFunc<int> freeFunc;
	shared_ptr<int> sp1((int*)malloc(4), freeFunc);
	DeleteArrayFunc<int> deleteArrayFunc;
	shared_ptr<int> sp2((int*)malloc(4), deleteArrayFunc);
	return 0;
}

The second parameter can be passed to a generic function, which represents the way of deconstruction.

Published 54 original articles, won praise 5, visited 2239
Private letter follow

Keywords: network

Added by Mountain Water on Wed, 26 Feb 2020 10:08:09 +0200