Technology Sharing

Experience the power of smart pointers

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina


Course Catalog



1. Basic knowledge of smart pointers

Disadvantages of naked pointers:

  1. forgetdeleteReleasing resources leads to resource leakage
  2. existdeleteThe program exited normally before (for example, ififmiddlereturn) or abnormal exit, before it candelete, resulting in resource leakage
  3. The same resource is released multiple times, resulting in the release of wild pointers and program crashes

At this time, smart pointers are needed. The smartness of smart pointers is mainly reflected in the fact that users do not need to pay attention to the release of resources, because smart pointers will help you completely manage the release of resources. It will ensure that no matter how the program logic runs, it will execute normally or produce exceptions.When resources expire (function scope or program ends), they will be released

Let's implement a simple smart pointer ourselves

template<typename T>
class CSmartPtr
{
public:
	CSmartPtr(T* ptr = nullptr) : mptr(ptr) {}
	~CSmartPtr() { delete mptr; }
private:
	T* mptr;
};

int main()
{
	CSmartPtr<int> ptr(new int());
	/*其它的代码...*/

	/*
	由于ptr是栈上的智能指针对象,不管是函数正常执行完,
	还是运行过程中出现异常,栈上的对象都会自动调用析构函数,
	在析构函数中进行了delete操作,保证释放资源
	*/

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  1. Smart pointers are object-oriented encapsulation of raw pointers, initializing resource addresses in constructors and releasing resources in destructors.
  2. By taking advantage of the fact that objects on the stack are automatically destructed when they go out of scope, resources are released in the destructor of the smart pointer.

Therefore, due to the above characteristics, smart pointers are generally defined on the stack.A smart pointer is a class object, a pointer is passed into the constructor of this class, and the passed pointer is released in the destructor. Since the class object is allocated and released on the stack, it will be automatically released when our function (or program) ends.

So, can we define smart pointers on the heap? For exampleCSmartPtr* p = new CSmartPtr(new int);, the compilation is successful, but the definition here ispAlthough it is a smart pointer type, it is essentially a raw pointer, sopStill need to manuallydelete, which brings us back to the problem we faced with raw pointers at the beginning, so don't use it like this

Of course, smart pointers should be similar to raw pointers and provide the common*and->The overloaded functions of the two operators are used just like naked pointers. The code is as follows:

template<typename T>
class CSmartPtr
{
public:
	CSmartPtr(T* ptr = nullptr) : mptr(ptr) {}
	~CSmartPtr() { delete mptr; }

	T& operator*() { return *mptr; }
	T* operator->() { return mptr; }

private:
	T* mptr;
};


int main()
{
	CSmartPtr<int> ptr(new int());

	*ptr = 20;	// operator*()一定要返回引用,这样才可以赋值

	cout << *ptr << endl;	// 20

	class Test
	{
	public:
		void test() { cout << "call Test::test()" << endl; }
	};

	CSmartPtr<Test> ptr2 = new Test();

	(*ptr2).test();	// (*ptr2)取出Test对象,用对象调用方法

	// operator->()返回的是一个指针,实现了用指针调用函数
	// 即(ptr2.operator->()) -> test()
	ptr2->test();

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

2. Smart pointer without reference counting

The smart pointer implemented in the previous section is very similar to an ordinary naked pointer in use, but it still has a big problem. See the following code:

CSmartPtr<int> p1(new int());
CSmartPtr<int> p2(p1);	// 拷贝构造
  • 1
  • 2

Running the code crashes directly because the default copy constructor does a shallow copy.p1andp2Holding the samenew intresource,p2First, the destructor releases the resources, thenp1When destructed, it becomesdeleteWild pointer, program crashes

So, how to solve the problem of shallow copy? RewriteCSmartPtrThe copy constructor

CSmartPtr(const CSmartPtr<T>& src) { mptr = new T(*src.mptr); }
  • 1

Now the code runs fine, butp1andp2They manage two different resources. If users do not understand, they may mistakenly thinkp1andp2Managing the same resource

Therefore, it is wrong to write the copy constructor like this

So, how to solve the shallow copy problem of smart pointers? Two methods:Smart pointer without reference counting, smart pointer with reference counting

In this section, we first look atSmart pointer without reference counting

  1. auto_ptr(C++98, now deprecated)
  2. scoped_ptr(Boost library)
  3. unique_ptr(C++11, recommended)

Include the header file when using:#include <memory>

auto_ptr

Let's consider this code first.

auto_ptr<int> ptr1(new int());
auto_ptr<int> ptr2(ptr1);

*ptr2 = 20;
cout << *ptr1 << endl;
  • 1
  • 2
  • 3
  • 4
  • 5

Run crash, why? Let's take a lookauto_ptrSource code

insert image description here

You can see that the copy construction will call the passed objectreleasemethod, this method is to_RightThe resource pointed to is returned to the newauto_ptrobject, and_Right(oldauto_ptr)of_MyptrSet tonullptr

In short, the oldauto_ptrPointing to new resourcesauto_ptrHold, old setnullptrTherefore, the code above uses*ptr1It's just wrong.auto_ptrAlways let the last smart pointer manage resources)

So,auto_ptrCan it be used in a container? See the following code

int main()
{
	vector<auto_ptr<int>> vec;
	vec.push_back(auto_ptr<int>(new int(10)));
	vec.push_back(auto_ptr<int>(new int(20)));
	vec.push_back(auto_ptr<int>(new int(30)));
	
	cout << *vec[0] << endl;	// 10
	
	vector<auto_ptr<int>> vec2 = vec;
	/* 这里由于上面做了vector容器的拷贝,
	相当于容器中的每一个元素都进行了拷贝构造,
	原来vec中的智能指针全部为nullptr了,
	再次访问就成访问空指针了,程序崩溃
	*/
	
	cout << *vec[0] << endl;	// 程序崩溃
	
	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

Therefore, it is not recommended to use in C++auto_ptr, unless the application scenario is very simple. Andauto_ptrDeprecated in C++11 and completely removed in C++17

Summarize:auto_ptrSmart pointers do not have reference counting, so they handle the shallow copy problem by directly copying the previousauto_ptrSet tonullptr, only the last oneauto_ptrHolding resources

scoped_ptr

You need to install the Boost library, which includes the header files#include <boost/scoped_ptr.hpp>Ready to use

take a lookscoped_ptrSource code:

template<class T> class scoped_ptr
{
private:
	scoped_ptr(scoped_ptr const&);
	scoped_ptr& operator=(scoped_ptr const&);
...
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

can be seenscoped_ptrThe copy constructor and assignment function are privatized, so the object does not support these two operations and cannot be called, which fundamentally eliminates the occurrence of shallow copies.

soscoped_ptrIt cannot be used in containers. If containers copy or assign values ​​to each other, it will causescoped_ptrObject copy construction and assignment function, compilation error

scoped_ptrandauto_ptrThe difference: can be explained by ownership,auto_ptrThe ownership of resources can be transferred arbitrarily, andscoped_ptrNo ownership is transferred (because copy construction and assignment functions are prohibited)

scoped_ptrGenerally not used

unique_ptr

Take a look firstunique_ptrPart of the source code:

template<class _Ty, class _Dx>
class unique_ptr
{
public:
	/*提供了右值引用的拷贝构造函数*/
	unique_ptr(unique_ptr&& _Right) { ... }
	
	/*提供了右值引用的operator=赋值重载函数*/
	unique_ptr& operator=(unique_ptr&& _Right) { ... }

	/*
	删除了unique_ptr的拷贝构造和赋值函数,
	因此不能做unique_ptr智能指针对象的拷贝构造和赋值,
	防止浅拷贝的发生
	*/
	unique_ptr(const unique_ptr&) = delete;
	unique_ptr& operator=(const unique_ptr&) = delete;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

As seen from above,unique_ptrA little bitscoped_ptrThe same thing is done, that is, copy construction and assignment overload functions are disabled, prohibiting users fromunique_ptrPerform explicit copy construction and assignment to prevent the occurrence of shallow copy problems of smart pointers.

Since there is no copy constructor,unique_ptr<int> p1(new int()); unique_ptr<int> p2(p1);Nature is wrong

butunique_ptrProvides copy construction and assignment functions with rvalue reference parameters, that is,unique_ptrSmart pointers can be copied and assigned via right-value references, or when generatingunique_ptrTemporary objects, such asunique_ptrAs the return value of the function, the sample code is as follows:

// 示例1
unique_ptr<int> p1(new int());
unique_ptr<int> p2(move(p1));	// 使用了右值引用的拷贝构造
p2 = move(p1);	// 使用了右值引用的operator=赋值重载函数
  • 1
  • 2
  • 3
  • 4
// 示例2
unique_ptr<int> test_uniqueptr()
{
	unique_ptr<int> ptr(new int());
	return ptr;
}
int main()
{
	/*
	此处调用test_uniqueptr函数,在return ptr代码处,
	调用右值引用的拷贝构造和赋值函数
	*/
	unique_ptr<int> p = test_uniqueptr();	// 调用带右值引用的拷贝构造函数
	p = test_uniqueptr();	// 调用带右值引用的operator=赋值重载函数
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Then useunique_ptrThe benefit is that users canunique_ptr<int> p2(move(p1));It can be clearly seen from the statementp1Transferred resources top2p1No resources are held. If usedauto_ptrIf you do not explicitlymoveWhen written out, the intention is not obvious, and if you don't understand the underlying structure, you will use it incorrectly.

At the same time, fromunique_ptrAs the name indicates, only one smart pointer can reference a resource. Therefore, it is recommended to give priority to smart pointers without reference counting.unique_ptr

3. Smart pointer with reference counting

Smart pointers with reference counting mainly includeshared_ptrandweak_ptr, with reference countingbenefitThat is, multiple smart pointers can manage the same resource, so what is a smart pointer with reference counting?

With reference counting: Match a reference count to each object's resources

When multiple smart pointers are allowed to point to the same resource, each smart pointer will add 1 to the reference count of the resource. When a smart pointer is destructed, the reference count of the resource will also be reduced by 1. When the last smart pointer reduces the reference count of the resource from 1 to 0, it means that the resource can be released. The destructor of the last smart pointer will handle the release of the resource. This is the concept of reference counting.

  • When the reference countSubtracting 1 does not equal 0When the current smart pointer does not use this resource, but there are other smart pointers using this resource, the current smart pointer cannot destruct this resource and can only go directly
  • When the reference countSubtract 1 to get 0, it means that the current smart pointer is the last smart pointer to use this resource, so it is responsible for releasing this resource.

Simulate a smart pointer with reference counting

Take the previous implementationCSmartPtrMake modifications and directly upload the code:

// 对资源进行引用计数的类
template<typename T>
class RefCnt
{
public:
	RefCnt(T* ptr = nullptr) : mptr(ptr)
	{
		if (mptr != nullptr)
			mcount = 1;
	}
	void addRef() { mcount++; }	// 增加资源的引用计数
	int delRef() { return --mcount; }

private:
	T* mptr;
	int mcount;
};

template<typename T>
class CSmartPtr
{
public:
	CSmartPtr(T* ptr = nullptr) : mptr(ptr)
	{
		// 智能指针构造的时候给资源建立引用计数对象
		mpRefCnt = new RefCnt<T>(mptr);
	}
	~CSmartPtr()
	{
		if (0 == mpRefCnt->delRef())
		{
			delete mptr;
			mptr = nullptr;
		}
	}

	// 实现拷贝构造
	CSmartPtr(const CSmartPtr<T>& src)
		:mptr(src.mptr), mpRefCnt(src.mpRefCnt)
	{
		if (mptr != nullptr)
			mpRefCnt->addRef();
	}
	CSmartPtr<T>& operator=(const CSmartPtr<T>& src)
	{
		// 防止自赋值
		if (this == &src)
			return *this;

		// 本身指向的资源减1
		// 如果减1为0释放资源;如果减1不为0直接走
		if (0 == mpRefCnt->delRef()) { delete mptr; }

		mptr = src.mptr;
		mpRefCnt = src.mpRefCnt;
		mpRefCnt->addRef();
		return *this;
	}

	T& operator*() { return *mptr; }
	T* operator->() { return mptr; }

private:
	T* mptr;	// 指向资源的指针
	RefCnt<T>* mpRefCnt;	// 指向该资源引用计数对象的指针
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
// 那么现在就不会报错了,不会对同一个资源释放多次
CSmartPtr<int> ptr1(new int());
CSmartPtr<int> ptr2(ptr1);
CSmartPtr<int> ptr3;
ptr3 = ptr2;

*ptr1 = 20;
cout << *ptr2 << " " << *ptr3 << endl;	// 20 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

This enables multiple smart pointers to manage the same resource

However, the smart pointers we have implemented are not thread-safe and cannot be used in multi-threaded scenarios.shared_ptrandweak_ptrIt is thread safe!

shared_ptr cross reference issue

  • shared_ptrpowerfulSmart pointers (can change the reference count of a resource)
  • weak_ptrweakSmart pointers (do not change the reference count of the resource)

We implemented theCSmartPtrIt is also a strong smart pointer (can change the reference count of resources)

It can be understood like this: Weak smart pointer observe strong smart pointer, strong smart pointerobserve Resources (memory)

So,Cross-reference (circular reference) problem of strong smart pointerWhat is it? Let's take a look

class B; // 前置声明类B
class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
	shared_ptr<B> _ptrb;	// 指向B对象的智能指针
};
class B
{
public:
	B() { cout << "B()" << endl; }
	~B() { cout << "~B()" << endl; }
	shared_ptr<A> _ptra;	// 指向A对象的智能指针
};

int main()
{
	shared_ptr<A> pa(new A());	// pa指向A对象,A的引用计数为1
	shared_ptr<B> pb(new B());	// pb指向B对象,B的引用计数为1
	pa->_ptrb = pb;	// A对象的成员变量_ptrb也指向B对象,B的引用计数为2
	pb->_ptra = pa;	// B对象的成员变量_ptra也指向A对象,A的引用计数为2

	cout << pa.use_count() << endl;
	cout << pb.use_count() << endl;

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

operation result:

A()
B()
2
2
  • 1
  • 2
  • 3
  • 4

As you can see, A and B have no destructors, which is why cross-references can causenewThe resources that come out cannot be released, resulting in resource leakage!

analyze:
insert image description hereinsert image description here
outmainFunction scope,paandpbThe two local objects are destroyed, and the reference counts of objects A and B are reduced from 2 to 1 respectively. The conditions for releasing A and B are not met (the condition for release is that the reference counts of A and B are reduced to 0), so twonewThe A and B objects that come out cannot be released, resulting in memory leaks. This problem isCross-reference (circular reference) problem of strong smart pointer

Solution: Use strong smart pointers when defining objects and weak smart pointers when referencing objects

class B; // 前置声明类B
class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
	weak_ptr<B> _ptrb;	// 指向B对象的弱智能指针(引用对象时,用弱智能指针)
};
class B
{
public:
	B() { cout << "B()" << endl; }
	~B() { cout << "~B()" << endl; }
	weak_ptr<A> _ptra;	// 指向A对象的弱智能指针(引用对象时,用弱智能指针)
};


int main()
{
	// 定义对象时,用强智能指针
	shared_ptr<A> pa(new A());	// pa指向A对象,A的引用计数为1
	shared_ptr<B> pb(new B());	// pb指向B对象,B的引用计数为1

	// A对象的成员变量_ptrb也指向B对象,B的引用计数为1,因为是弱智能指针,引用计数没有改变
	pa->_ptrb = pb;
	// B对象的成员变量_ptra也指向A对象,A的引用计数为1,因为是弱智能指针,引用计数没有改变
	pb->_ptra = pa;

	cout << pa.use_count() << endl; // 1
	cout << pb.use_count() << endl; // 1

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

operation result:

A()
B()
1
1
~B()
~A()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

As you can see,mainFunction scope,paandpbThe two local objects are destroyed, and the reference counts of objects A and B are reduced from 1 to 0, respectively, meeting the conditions for releasing A and B. ThereforenewThe A and B objects that came out were destructed, solving the problemCross-reference (circular reference) problem of strong smart pointer

insert image description here
can be seen,weak_ptrA weak smart pointer does not change the reference count of a resource, that is, a weak smart pointer only observes whether the object is alive (whether the reference count is 0) and cannot use the resource.

Then if we addvoid testA() { cout << "非常好用的方法!!" << endl; }Such a method adds in Bvoid func() { _ptra->testA(); }Is it OK to call this method?

  • Can't, because the weak smart pointer is just an observer and cannot use resources, that is, it does not provide*and->Operator overloads cannot use functions similar to naked pointers

Solution:

class B; // 前置声明类B
class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
	void testA() { cout << "非常好用的方法!!" << endl; }
	weak_ptr<B> _ptrb;	// 指向B对象的弱智能指针(引用对象时,用弱智能指针)
};
class B
{
public:
	B() { cout << "B()" << endl; }
	~B() { cout << "~B()" << endl; }
	void func()
	{
		shared_ptr<A> sp = _ptra.lock(); // 提升方法,出函数作用域就自动析构了
		if (sp != nullptr)
			sp->testA();
	}
	weak_ptr<A> _ptra;	// 指向A对象的弱智能指针(引用对象时,用弱智能指针)
};

int main()
{
	shared_ptr<A> pa(new A());
	shared_ptr<B> pb(new B());
	pa->_ptrb = pb;
	pb->_ptra = pa;
	cout << pa.use_count() << endl; // 1
	cout << pb.use_count() << endl; // 1

	pb->func();

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

Although weak_ptrdoes not own the object, but it can be accessed throughlock()Method attempts to obtain a pointer to an objectshared_ptr, if the object is still alive (i.e. there are othershared_ptrpoint to it),lock()A pointer to the object will be returnedshared_ptrOtherwise, an emptyshared_ptr

uselock()A typical scenario for a method is when temporary access is requiredweak_ptrWhen you point to an object, you do not want to increase the lifetime count of the object.

operation result:

A()
B()
1
1
非常好用的方法!!
~B()
~A()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Now you can see that it was called correctly!

4. Thread safety issues of multi-threaded access to shared objects

Let’s look at oneThread safety issues when multiple threads access shared objects:Thread A and Thread B access a shared object. If Thread A is destructing the object and Thread B wants to call a member method of the shared object, Thread A may have already destructed the object. If Thread B accesses the object again, an unexpected error will occur.

First look at the following code:

class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
	void testA() { cout << "非常好用的方法!!" << endl; }
};

// 子线程
void handler01(A* q)
{
	// 睡眠两秒,此时main主线程已经把A对象给delete析构掉了
	std::this_thread::sleep_for(std::chrono::seconds(2));
	q->testA();
}

// main线程
int main()
{
	A* p = new A();
	thread t1(handler01, p);
	delete p;
	//阻塞当前线程,直到调用join()的线程结束
	t1.join();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

In executionq->testA();When this statement is made,mainThe thread has already destroyed the shared object, which is obviously unreasonable

If you want to passqIf the pointer wants to access object A, it needs to determine whether object A is alive. If object A is alive, calltestAThere is no problem with the method; if the A object has been destroyed, callingtestAThere is a problem! That is to sayqWhen accessing object A, we need to detect whether object A is alive. How to solve this problem? We need to use strong and weak smart pointers.

Let’s look at this code:

// 子线程
void handler01(weak_ptr<A> wp)
{
	// 睡眠两秒
	std::this_thread::sleep_for(std::chrono::seconds(2));
	shared_ptr<A> sp = wp.lock();
	if (sp != nullptr)
		sp->testA();
	else
		cout << "A对象已经析构,不能再访问!" << endl;
}

// main线程
int main()
{
	shared_ptr<A> p(new A());
	thread t1(handler01, weak_ptr<A>(p));
	t1.join();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

operation result:

A()
非常好用的方法!!
~A()
  • 1
  • 2
  • 3

You can see it running.sp->testA();,becausemainThe thread calledt1.join()The method waits for the child thread to end.wppasslockSuccessfully promoted tosp

Modify the above code:

// 子线程
void handler01(weak_ptr<A> wp)
{
	// 睡眠两秒
	std::this_thread::sleep_for(std::chrono::seconds(2));
	shared_ptr<A> sp = wp.lock();
	if (sp != nullptr)
		sp->testA();
	else
		cout << "A对象已经析构,不能再访问!" << endl;
}

// main线程
int main()
{
	{
		shared_ptr<A> p(new A());
		thread t1(handler01, weak_ptr<A>(p));
		t1.detach();
	}
	std::this_thread::sleep_for(std::chrono::seconds(3));
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

operation result:

A()
~A()
A对象已经析构,不能再访问!
  • 1
  • 2
  • 3

As you can see, we set a scope and sett1To separate threads, let smart pointerspOut of scope to destruct A, then it will printA对象已经析构,不能再访问!, that iswppasslockFailed to upgrade tosp

The above is the thread safety issue of accessing shared objects in multiple threads.shared_ptrandweak_ptrA typical application of.

5. Smart Pointer Deleter

6. It is recommended to use make_shared instead of shared_ptr


Reference articles:In-depth understanding of C++ smart pointers