Interpretation of smart pointers (2)

In the previous article, I explained the principle of smart pointers and implemented a simple smart pointer. In order to deepen the understanding of smart pointers, in this article, I explain several smart pointers in C++: auto_ptr, unique_ptr, shared_ptr, weak_ptr.

1、auto_ptr

In the previous article, we disabled the copy constructor and assignment operator of smart_ptr. How is auto_ptr handled in the copy constructor and assignment operator? It will assign the original pointer to nullptr.

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

	}

	auto_ptr(auto_ptr& _ap) : ptr(_ap.ptr) {
		_ap.ptr = nullptr;
	}

	auto_ptr<T>& operator = (auto_ptr<T>& _ap) {
		if (ptr != _ap.ptr) {
			ptr = _ap.ptr;
			_ap.ptr = nullptr;
		}

		return *this;
	}

	~auto_ptr() {
		delete ptr;
		ptr = nullptr;
	}

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

	T* operator -> () {
		return ptr;
	}
private:
	T* ptr;
};

3、unique_ptr

unique_ptr is a smart pointer provided in the c++11 version library, which directly disables the copy constructor and assignment overloaded function. So unique_ptr can only be moved, not assigned.

4、shared_ptr

shared_ptr allows multiple smart pointers to point to the same resource and ensures that the shared resource will only be released once.
shared_ptr uses the reference counting principle to realize resource sharing between multiple shared_ptr objects:
(1) Reference counting is used to record that the resource is shared by several objects.
(2) When a shared_ptr object is destroyed (the destructor is called), the count will be decremented by 1 in the destructor.
(3) If the reference count is reduced to 0, it means that it is the last shared_ptr object to use the resource and must release the resource.
(4) If the reference count is not 0, it means that there are other objects in use, and the resource cannot be released.

When using shared_ptr, be careful not to let two shared_ptr point to the same raw pointer, for example:

A* p = new A(10);
shared_ptr<A> sp1(p), sp2(p);

sp1 and sp2 will not share the same reference count for p, but each will record the reference count of p as 1 (sp2 cannot know that p has been managed by sp1). In this way, p will be destructed when sp1 dies, and p will be destructed again when sp2 dies, which will cause the program to crash.

5、weak_ptr

Objects of the weak_ptr class can point to shared_ptr, but the reference count of shared_ptr will not be changed. Once the last shared_ptr is destroyed, the object will be released.
weak_ptr does not overload the operator-> and operator * operators, so the object cannot be used directly through weak_ptr. The typical usage is to call its lock function to obtain the shared_ptr example, and then access the original object.

Let's look at an example of shared_ptr cross-reference:

class B;
class A
{
public:
	shared_ptr<B> sp;

	~A(){
		cout << "~A"<<endl;
	}
};
class B
{
public:
	shared_ptr<A> sp;
	~B(){
		cout << "~B"<<endl;
	}
};

void fun() {
	shared_ptr<B> pb(new B());
	shared_ptr<A> pa(new A());

	pb->sp = pa;
	cout << "pb.use_count " << pb.use_count() << endl;//1
	cout << "pa.use_count " << pa.use_count() << endl;//2

	pa->sp = pb;
	cout << "pb.use_count " << pb.use_count() << endl;//2
	cout << "pa.use_count " << pa.use_count() << endl;//2

    //并没有输出 ~A, ~B,也就是class B;
class A
{
public:
	shared_ptr<B> sp;

	~A(){
		cout << "~A"<<endl;
	}
};
class B
{
public:
	shared_ptr<A> sp;
	~B(){
		cout << "~B"<<endl;
	}
};

void fun() {
	shared_ptr<B> pb(new B());
	shared_ptr<A> pa(new A());

	pb->sp = pa;
	cout << "pb.use_count " << pb.use_count() << endl;//1
	cout << "pa.use_count " << pa.use_count() << endl;//2

	pa->sp = pb;
	cout << "pb.use_count " << pb.use_count() << endl;//2
	cout << "pa.use_count " << pa.use_count() << endl;//2

    //没有输出~A, ~B。也就是没有调用A和B的析构函数。
}

How to avoid this cross-reference? This requires the use of weak_ptr: change the shared_ptr<B> sp in A to weak_ptr<B> sp_weak, so that the value of the sp reference count use_count() will not be increased when passing, so the A and B resources can be released normally:

class B;
class A
{
public:
	//shared_ptr<B> sp;
	weak_ptr<B> sp_weak;

	~A(){
		cout << "~A"<<endl;
	}
};
class B
{
public:
	shared_ptr<A> sp;
	~B(){
		cout << "~B"<<endl;
	}
};

void fun() {
	shared_ptr<B> pb(new B());
	shared_ptr<A> pa(new A());

	pb->sp = pa;
	cout << "pb.use_count " << pb.use_count() << endl;//1
	cout << "pa.use_count " << pa.use_count() << endl;//2

	//pa->sp = pb;
	pa->sp_weak = pb;
	cout << "pb.use_count " << pb.use_count() << endl;//1
	cout << "pa.use_count " << pa.use_count() << endl;//2

	shared_ptr<B> pa2 = pa->sp_weak.lock();

}

Guess you like

Origin blog.csdn.net/mars21/article/details/133021517