C++11:智能指针

Posted 给算法爸爸上香

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++11:智能指针相关的知识,希望对你有一定的参考价值。

智能指针是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保在离开指针所在作用域时,自动正确地销毁动态分配的对象,防止内存泄露。它的一种通用实现技术是使用引用计数。每使用它一次,内部的引用计数加1,每析构一次,内部引用计数减1,减为0时,删除所指向的堆内存。
C++11提供了3种智能指针: std:.shared_ptr、std::uniq _ptr和std::weak _ptr,使用时需要引用头文件,本章将分别介绍这3种智能指针。

目录

1 shared_ptr共享的智能指针

std:.shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。在最后一个shared_ptr析构的时候,内存才会被释放。

1.1shared_ptr的基本用法

初始化:
可以通过构造函数、std::make_shared辅助函数和reset方法来初始化shared_ptr,代码如下:

std::shared_ptr<int> p(new int(1));
std::shared_ptr<int> p2 = p;
std::shared_ptr<int> ptr;
ptr.reset(new int (1)) ;
if (ptr)

	cout<< "ptr is not null";

我们应该优先使用make_shared来构造智能指针,因为它更加高效。
不能将一个原始指针直接赋值给一个智能指针,例如,下面这种方法是错误的:

std::shared_ptr<int> p = new int(1);	//编译报错,不允许直接赋值

可以看到智能指针的用法和普通指针的用法类似,只不过不需要自己管理分配的内存。shared_ptr不能通过直接将原始指针赋值来初始化,需要通过构造函数和辅助方法来初始化。对于一个未初始化的智能指针,可以通过reset方法来初始化,当智能指针中有值的时候,调用reset 会使引用计数减1。另外,智能指针可以通过重载的bool类型操作符来判断智能指针中是否为空(未初始化)。
获取原始指针:
当需要获取原始指针时,可以通过get方法来返回原始指针,代码如下:

std::shared_ptr<int> ptr (new int (1));
int* p=ptr.get () ;

指定删除器:
智能指针初始化可以指定删除器。

void DeleteIntPtr (int* p)

	delete p;

std::shared_ptr<int> p(newint, DeleteIntPtr);

当p的引用计数为0时,自动调用删除器DeleteIntPtr来释放对象的内存。删除器可以是一个 lambda表达式,因此,上面的写法还可以改为:

std::shared_ptr<int> p(new int, [](int* p) delete p; );

当我们用shared_ptr管理动态数组时,需要指定删除器,因为std::shared_ptr的默认删除器不支持数组对象,代码如下:

std::shared_ptr<int> p(new int[10], [](int* p)delete[] p;));	//指定delete[]

也可以将std::default_delete作为删除器。default_delete的内部是通过调用delete来实现功能的,代码如下:

std::shared_ptr<int> p(new int[10], std::default_delete<int[]>);

另外,还可以通过封装一个make_shared_array方法来让 shared_ptr支持数组,代码如下:

template<typename T>
shared_ptr<T> make_shared_array(size_t size)

	return shared_ptr<T>(new T[size], default_delete<T[]>());


std::shared_ptr<int> p = make_shared_array<int>(10);
std::shared_ptr<char> p = make_shared_array<char>(10);

1.2 使用shared _ptr 需要注意的问题

智能指针虽然能自动管理堆内存,但它还是有不少陷阱,在使用时要注意。
1)不要用一个原始指针初始化多个shared_ptr,例如下面这些是错误的:

int* ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr) ; 	//logic error

2)不要在函数实参中创建shared_ptr。对于下面的用写法:

f(shared_ptr<int>(new int), g());	//有缺陷

因为C++的函数参数的计算顺序在不同的编译器不同的调用约定下可能是不一样的,一般是从右到左,但也有可能是从左到右,所以,可能的过程是先new int,然后调g(),如果恰好g()发生异常,而shared_ptr还没有创建,则int 内存泄露了,正确的写法应该是先创建智能指针,代码如下:

shared_ptr<int> p(new int());
f(p, g());

3)通过shared_from_this()返回this指针。不要将this指针作为shared_ptr返回出来,因为this 指针本质上是一个裸指针,因此,这样可能会导致重复析构,看下面的例子:

struct A

	shared_ptr<A>GetSelf()
	
		return shared_ptr<S>(this);	//don't do this!
	
;

int main ()

	shared_ptr<A> spl(new A);
	shared_ptr<A> sp2 = sp1->Getself();
	return0;

在这个例子中,由于用同一个指针( this)构造了两个智能指针sp1和 sp2,而它们之间是没有任何关系的,在离开作用域之后this将会被构造的两个智能指针各自析构,导致重复析构的错误。
正确返回this的 shared_ptr 的做法是:让目标类通过派生std::enable_shared_from_this类,然后使用基类的成员函数shared_from_this来返回this的 shared_ptr,看下面的示例:

class A: public std::enable_shared_from_this<A>

	std::shared_ptr<A> Getself()
	
		return shared_from_this();
	
;

std::shared_ptr<A> spy(newA)
std::shared_ptr<A> p = spy->GetSelf();	//OK

至于用shared_from_this()的原因,将在4.3.3节中给出解释。
4)要避免循环引用。智能指针最大的一个陷阱是循环引用,循环引用会导致内存泄露。下例是一个典型的循环引用的场景。

struct A

	std::shared_ptr<B> bptr;
	~A()	 cout << "A is deleted ! " <<endl; 
;

struct B

	std::shared_ptr<A> aptr;
	~B()	 cout << "B is deleted! " << endl; 
;

void TestPtr()

	std::shared_ptr<A> ap(new A);
	std::shared_ptr<B> bp(new B);
	ap->bptr = bp;
	bp->aptr = ap;

测试结果是两个指针A和B都不会被删除,存在内存泄露。循环引用导致ap和 bp 的引用计数为2,在离开作用域之后,ap和 bp 的引用计数减为1,并不会减为0,导致两个指针都不会被析构,产生了内存泄露。解决办法是把A和B任何一个成员变量改为weak_ptr。

2 unique_ptr独占的智能指针

unique_ptr是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另外一个unique_ptr。下面这样是错误的:

unique_ptr<T> myPtr(new T);
unique_ptr<T> myotherPtr = myPtr;	//错误,不能复制

unique_ptr不允许复制,但可以通过函数返回给其他的unique_ptr,还可以通过std::move来转移到其他的unique_ptr,这样它本身就不再拥有原来指针的所有权了。例如:

unique_ptr<T> myPtr(new T);	//正确
unique_ptr<T> myotherPtr = std::move(myPtr);	//正确
unique_ptr<T> ptr = myPtr;	//错误,只能移动,不可复制

unique_ptr和 shared_ptr相比,unique_ptr除了独占性这个特点之外,还可以指向一个数组,代码如下:

std::unique_ptr<int []> ptr(new int [10]);
ptr[9] = 9;	//设置最后一个元素值为9

std::shared_ptr<int []> ptr(new int[10]);是不合法的。
unique_ptr指定删除器和 std::shared_ptr是有差别的,比如下面的写法:

std::shared_ptr<int> ptr(new int(1), [](int*p)delete p;);	//正确
std::unique_ptr<int> ptr(new int(1), [](int*p)delete p;);	//错误

std:.unique_ptr指定删除器的时候需要确定删除器的类型,所以不能像shared_ptr那样直接指定删除器,可以这样写:

std::unique_ptr<int, void(*)(int*)> ptr(new int(1), [](int*p)delete p; );

上面这种写法在 lambda没有捕获变量的情况下是正确的,如果捕获了变量,则会编译报错:

std::unique ptr<int, void(*)(int*)> ptr(new int(1), [&](int*p)delete p;);	//错误,捕获了变量

这是因为lambda在没有捕获变量的情况下是可以直接转换为函数指针的,一旦捕获了就无法转换了。
如果希望unique_ptr的删除器支持lambda,可以这样写:

std::unique_ptr<int, std::function<void(int*)>> ptr(new int(1), [&](int*p)delete p;);

还可以自定义unique_ptr的删除器,比如下面的代码:

#include <memory>
#include <functional>
using namespace std;

struct MyDeleter

	void operator() (int*p)
	
		cout<<"delete"<<endl;
		delete p;
	
;

int main()

	std::unique_ptr<int, MyDeleter> p(new int(1));
	return 0;

关于shared_ptr和 unique_ptr的使用场景要根据实际应用需求来选择,如果希望只有一个智能指针管理资源或者管理数组就用unique_ptr,如果希望多个智能指针管理同一个资源就用shared_ptr。

3 weak_ptr弱引用的智能指针

弱引用指针weak_ptr是用来监视shared_ptr 的,不会使引用计数加1,它不管理shared_ptr内部的指针,主要是为了监视 shared_ptr的生命周期,更像是 shared_ptr 的一个助手。weak_ptr没有重载操作符*和->,因为它不共享指针,不能操作资源,主要是为了通过shared_ptr获得资源的监测权,它的构造不会增加引用计数,它的析构也不会减少引用计数,纯粹只是作为一个旁观者来监视 shared_ptr中管理的资源是否存在。weak_ptr还可以用来返回this指针和解决循环引用的问题。

3.1 weak_ptr基本用法

1)通过use_count()方法来获得当前观测资源的引用计数,代码如下:

shared_ptr<int> sp(new int(10));
weak_ptr<int> wp(sp) ;
cout<<wp.use_count()<<endl;	//结果将输出1

2)通过expired()方法来判断所观测的资源是否已经被释放,代码如下:

shared_ptr<int> sp(new int(10));
weak_ptr<int> wp(sp) ;
if(wp.expired())
	std::cout <<"weak_ptr无效,所监视的智能指针已被释放\\n";
else
	std::cout<<"weak_ptr有效\\n";	//结果将输出:weak_ptr有效

3 )通过lock方法来获取所监视的shared_ptr,代码如下:

std::weak_ptr<int> gw ;
void f ()

	if (gw.expired ()) 	//所监视的shared_ptr是否释放
	
		std::cout << "gw is expired\\n";
	
	else
	
		auto spt = gw.lock();
		std::cout << * spt <<" \\n";
	


int main ()

	
		auto sp = std::make_shared<int>(42);
		gw = sp;
		f();
	
	f();

输出如下:
42
gw is expired

3.2 weak_ptr返回this指针

之前提到不能直接将this指针返回为shared_ptr,需要通过派生std::enable_shared_from_this类,并通过其方法 shared_from_this来返回智能指针,原因是std:enableshared_from_this类中有一个weak _ptr,这个weak _ptr用来观测this智能指针,调用sharedfrom_this()方法时,会调用内部这个weak _ptr的 lock()方法,将所观测的shared_ptr返回。再看一下前面的例子。

struct A: public std::enable_shared_from_this<A>

	std::shared_ptr<A> GetSelf()
	
		return shared_from_this();
	
	~s ()
	
		cout <<"A is deleted"<< endl;
	
;
std::shared_ptr<A> spy(newA) ;
std::shared ptr<A> p = spy->Getself(); 	//OK

输出结果如下:
A is deleted
在外面创建A对象的智能指针和通过该对象返回this的智能指针都是安全的,因为shared_from_this()是内部的weak_ptr调用lock()方法之后返回的智能指针,在离开作用域之后,spy的引用计数减为0,A对象会被析构,不会出现A对象被析构两次的问题。
需要注意的是,获取自身智能指针的函数仅在shared_ptr的构造函数被调用之后才能使用,因为enable_shared_from_this内部的weak _ptr只有通过shared _ptr才能构造。

3.3 weak_ptr解决循环引用问题

智能指针的循环引用会导致内存泄露,再看一下前面的例子。

struct A

	std::shared_ptr<B> bptr;
	~A()  cout <<"A is deleted! " <<endl; 
;

struct B

	std::shared ptr<A> aptr;
	~B()  cout <<"B is deleted! " <<endl; 
) ;

void TestPtr()

	
		std::shared_ptr<A> ap (new A);
		std::shared_ptr<B> bp (new B);
		ap->bptr = bp;
		bp->aptr = ap;
		//objects should be destroyed .

在这个例子中,由于循环引用导致ap和 bp 的引用计数都为2,离开作用域之后引用计数减为1,不会去删除指针,导致内存泄露。通过weak_ptr就可以解决这个问题,只要将A或B的任意一个成员变量改为weak_ptr即可。

struct A

	std:: shared_ptr<B> bptr;
	~A() cout << "A is deleted! " << endl; 
 ;

struct B

	std::weak_ptr<A> aptr; 	//改为weak_ptr
	~B()  cout <<"B is deleted!" <<endl; 
;

void TestPtr()

	
		std::shared_ptr<A> ap (new A);
		std::shared_ptr<B> bp (new B);
		ap->bptr = bp;
		bp->aptr = ap;
		//objects should be destroyed .

输出如下:
A is deleted !
B is deleted !
这样在对B的成员赋值时,即执行bp->aptr=ap;时,由于aptr是 weak_ptr,它并不会增加引用计数,所以ap 的引用计数仍然会是1,在离开作用域之后,ap 的引用计数会减为0A指针会被析构,析构后其内部的 bptr的引用计数会减为1,然后在离开作用域后bp引用计数又从1减为0,B对象也将被析构,不会发生内存泄露。

4 通过智能指针管理第三方库分配的内存

智能指针可以很方便地管理当前程序库动态分配的内存,还可以用来管理第三方库分配的内存。第三方库分配的内存一般需要通过第三方库提供的释放接口才能释放,由于第三方库返回的指针一般都是原始指针,在用完之后如果没有调用第三方库的释放接口,就很容易造成内存泄露。比如下面的代码:

void* p = GetHandle ( ) ->create();
//do something. ..
GetHandle()->Release(p);

这段代码实际上是不安全的,在使用第三方库分配的内存的过程中,可能忘记调用Release接口,还有可能中间不小心返回了,还有可能中间发生了异常,导致无法调用Release接口。这时用智能指针来管理第三方库的内存就很合适了,只要离开作用域内存就会自动释放,不用显式去调用释放接口了,不用担心中途返回或者发生异常导致无法调用释放接口的问题。例如:

void* p = GetHandle ()->Create() ;
std::shared_ptr<void> sp(p, [this](void*p)  GetHandle()->Release(p); );

上面这段代码就可以保证任何时候都能正确释放第三方库分配的内存。虽然能解决问题,但还是有些烦琐,因为每个第三方库分配内存的地方都要调用这段代码。我们可以将这段代码提炼出来作为一个公共函数,简化调用:

std::shared_ptr<void> Guard(void* p)

	return std::shared_ptr<void> sp (p, [this](void*p)GetHandle()->Release(p););

void* p = GetHandle()->Create();
auto sp = Guard(p) ;
//do something. ..

上面的代码通过Guard 函数做了简化,用起来比较方便,但仍然不够安全,因为使用者可能会这样写:

void* p = GetHandle()->Create() ;
Guard(p); //危险,这句结束之后p就被释放了
//do something...

这样写是有问题的,会导致访问野指针,因为Guard§;是一个右值,如果不将这个右值赋值给一个指针,Guard§;这句结束之后,就会释放,导致p提前释放了,后面就会访问野指针的内容。auto sp=Guard§;需要一个赋值操作,忘记赋值就会导致指针提前释放,这种写法仍然不够安全。我们可以定义一个宏来解决这个问题,代码如下:

#define GUARD(P) std::shared_ptr<void> p##p(p, [](void*p)GetHandle()->Release(p);)

智能指针11

智能指针

智能指针存在的必要性

  1. malloc出来的空间,没有进行释放,存在内存泄漏的问题。
  2. 异常安全问题。如果在malloc和free之间如果存在抛异常,那么还是有内存 泄漏。这种问题就叫异常安全

智能指针的使用及原理

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
不需要显式地释放资源。
采用这种方式,对象所需的资源在其生命期内始终保持有效。

原理:
1. RAII特性
2. 重载operator*和opertaor->,具有像指针一样的行为

3. 解决浅拷贝的问题

C++98中的智能指针

auto_ptr:
edition1:
如何解决浅拷贝问题:资源转移。
缺点:转移资源,若此时想对原对象操作会出错。

namespace bit

	template<class T>
	class auto_ptr
	
      public:
	 auto_ptr(T* ptr=nullptr)
		 :_ptr(ptr)
	 
	 auto_ptr(auto_ptr<T>& pt):_ptr(pt._ptr)
	 
		 pt._ptr = nullptr;
	 
	 auto_ptr<T>& operator=(auto_ptr<T>& ap)
	 
		 if (this != &ap)
		 
			 // 此处需要将ap中的资源转移给this
			 // 但是不能直接转移,因为this可能已经管理资源了,否则就会造成资源泄漏
			 if (_ptr)
			 
				 delete _ptr;
			 

			 // ap就可以将其资源转移给this
			 _ptr = ap._ptr;
			 ap._ptr = nullptr;   // 让ap与之前管理的资源断开联系,因为ap中的资源已经转移给this了
		 

		 return *this;
	 
	 ~auto_ptr()
	 
		 if (_ptr)
		 
			 delete _ptr;
			 _ptr = nullptr;
		 
	 
	 T& operator*()
	 
		 return *_ptr;
	 
	 T* operator->()
	 
		 return _ptr;
	 
	 T* Get()
	 
		 return _ptr;
	 
	private:
		T* _ptr;
	;
;

editio2:转移释放权限

namespace bit

	template<class T>
	class auto_ptr
	
	public:
		auto_ptr(T* ptr =nullptr)
			:_ptr(ptr)
			,owner(false)
		
			if (_ptr)
			
				owner = true;
			
		
		auto_ptr(auto_ptr<T>& t)
			:_ptr(t._ptr)
			,owner(t.owner)
		
			t.owner = false;
		
		auto_ptr<T>& operator=(auto_ptr<T>& t)
		
			if (this != &t)
			
				if (this->_ptr && this->owner)
				
					delete _ptr;
				
				_ptr = t._ptr;
				owner = t.owner;
				t.owner = false;
			
			return *this;
		
		~auto_ptr()
		
			if (_ptr && owner)
			
				delete _ptr;
				owner = false;
				_ptr = nullptr;
			
		
		T& operator*()
		
			return *_ptr;
		
		T* operator->()
		
			return _ptr;
		
		T* Get()
		
			return _ptr;
		

	private:
		T* _ptr;
		bool owner;
	;

void TestAutoPtr()

	bit::auto_ptr<int> ap1(new int);
	*ap1 = 100;

	bit::auto_ptr<int> ap2(ap1);

	// 和我们对指针常规的认知有区别的
	int* p1 = new int;
	int* p2(p1);
	*p1 = 10;
	*p2 = 20;
	delete p1;
	p1 = p2 = nullptr;


	// auto_ptr采用资源转移的方式虽然将浅拷贝的问题解决了,但是引用了新的问题
	if (ap2.Get())
		*ap2 = 2000;
	if (ap1.Get())
		*ap1 = 1000;   // 代码会崩溃,因为ap1当中的资源已经转移走了,ap1当中的指针指向的空


	bit::auto_ptr<int> ap3(new int);
	*ap3 = 300;

	bit::auto_ptr<int> ap4(new int);
	*ap4 = 400;

	ap3 = ap4;

	//
	// 致命的缺陷---可以会导致野指针
	if (true)
	
		bit::auto_ptr<int> ap5(ap2);
		*ap5 = 100;
		*ap2 = 200;
		*ap1 = 300;

		// 再来开if的作用域时,ap5已经将管理的资源释放掉了
		// 而ap1和ap2根本就不知道,其内部的指针称为野指针了
	

	// 如果通过ap1和ap2再访问资源时,代码就会出问题
	*ap2 = 10;


int main()

	TestAutoPtr();
	return 0;

缺陷:可能会导致野指针的情况,比如上面的示例
所以:建议不要使用auto_ptr

C++11中的智能指针

C++11中unique_ptr指针

这个智能指针的作用就是防拷贝,既然拷贝可能会带来资源重复释放,资源泄露的问题,那我从根源上就不让你拷贝。

// unique_ptr: RAII + operator*()/operator->()+解决浅拷贝方式:禁止拷贝---资源独占
// 一份资源只能被一个对象来进行管理,对象之间不能共享资源

// 应用场景:只能应用与资源被一个对象管理 并且不会被共享的场景当中

// 缺陷:多个对象之间不能共享资源

// 负责释放new资源
template<class T>
class DFDef

public:
	void operator()(T*& ptr)
	
		if (ptr)
		
			delete ptr;
			ptr = nullptr;
		
	
;

// 负责:malloc的资源的释放
template<class T>
class Free

public:
	void operator()(T*& ptr)
	
		if (ptr)
		
			free(ptr);
			ptr = nullptr;
		
	
;

// 关闭文件指针
class FClose

public:
	void operator()(FILE*& ptr)
	
		if (ptr)
		
			fclose(ptr);
			ptr = nullptr;
		
	
;


namespace bite

	// T: 资源中所放数据的类型
	// DF: 资源的释放方式
	// 定制删除器
	template<class T, class DF = DFDef<T>>
	class unique_ptr
	
	public:
		/
		// RAII
		unique_ptr(T* ptr = nullptr)
			: _ptr(ptr)
		

		~unique_ptr()
		
			if (_ptr)
			
				// 问题:_ptr管理的资源:可能是从堆上申请的内存空间、文件指针、malloc空间...
				// delete _ptr; // 注意:此处的释放资源的方式不能写死了,应该按照资源类型不同找对应的方式释放
				// malloc--->free
				// new---->delete
				// fopen--->fclose关闭
				DF df;
				df(_ptr);
			
		

		
		// 具有指针类似的行为
		T& operator*()
		
			return *_ptr;
		

		T* operator->()
		
			return _ptr;
		

		T* Get()
		
			return _ptr;
		

		
		// 解决浅拷贝方式---资源独占,即防止被拷贝

		// C++98
		/*
		   将拷贝构造函数以及赋值运算符重载方法只声明不定义,并且需要将其权限设置为私有的

		   // 只声明不定义,没有将权限设置为private---不行的:因为方法别人可以再类外定义
		*/
	private:
		unique_ptr(const unique_ptr<T, DF>&);
		unique_ptr<T, DF>& operator=(const unique_ptr<T, DF>&);

		// C++11: 可以让编译器不生成默认的拷贝构造以及赋值运算符重载---delete
		// 在C++11当中,delete关键字的功能扩展:释放new申请的空间  用其修饰默认成员函数,表明:编译器不会生成了
		// unique_ptr(const unique_ptr<T,DF>&) = delete;  // 表明:编译器不会生成默认的拷贝构造函数
		// unique_ptr<T,DF>& operator=(const unique_ptr<T,DF>&) = delete;// 表明:编译器不会生成默认的赋值运算符重载
	private:
		T* _ptr;
	;

	// 用户在外部可以对方法进行定义---在unique_ptr的类中如果将该权限设置为private的
	//template<class T>
	//unique_ptr<T>::unique_ptr(const unique_ptr<T>& up)
	//



#include <memory>

void TestUniquePtr()

	bite::unique_ptr<int> up1(new int);
	bite::unique_ptr<int, Free<int>> up2((int*)malloc(sizeof(int)));
	bite::unique_ptr<FILE, FClose> up3(fopen("12345.txt", "w"));


int main()

	TestUniquePtr();

	bite::unique_ptr<int> up1(new int);
	// bite::unique_ptr<int> up2(up1);

	bite::unique_ptr<int> up3(new int);

	// up1 = up3;

	///
	// 标准库
	unique_ptr<int> up4(new int);
	// unique_ptr<int> up5(up4);
	unique_ptr<int> up6(new int);
	// up4 = up6;
	return 0;

C’++11中的shared_ptr

// 负责释放new资源
template<class T>
class DFDef

public:
	void operator()(T*& ptr)
	
		if (ptr)
		
			delete ptr;
			ptr = nullptr;
		
	
;

// 负责:malloc的资源的释放
template<class T>
class Free

public:
	void operator()(T*& ptr)
	
		if (ptr)
		
			free(ptr);
			ptr = nullptr;
		
	
;

// 关闭文件指针
class FClose

public:
	void operator()(FILE*& ptr)
	
		if (ptr)
		
			fclose(ptr);
			ptr = nullptr;
		
	
;


#include <mutex>

namespace bite

	// shared_ptr: 自身才是安全的---加锁:为了保证shared_ptr自身的安全性
	template<class T, class DF = DFDef<T>>
	class shared_ptr
	
	public:
		//
		// RAII
		shared_ptr(T* ptr = nullptr)
			: _ptr(ptr)
			, _pCount(nullptr)
			, _pMutex(new mutex)
		
			if (_ptr)
			
				// 此时只有当前刚刚创建好的1个对象在使用该份资源
				_pCount = new int(1);
			
		

		~shared_ptr()
		
			Release();
		

		/
		// 具有指针类似的行为
		T& operator*()
		
			return *_ptr;
		

		T* operator->()
		
			return _ptr;
		

		T* Get()
		
			return _ptr;
		

		//
		// 用户可能需要获取引用计数
		int use_count()const
		
			return *_pCount;
		

		///
		// 解决浅拷贝方式:采用引用计数
		shared_ptr(const shared_ptr<T>& sp)
			: _ptr(sp._ptr)
			, _pCount(sp._pCount)
			, _pMutex(sp._pMutex)
		
			AddRef();
		

		shared_ptr<T, DF>& operator=(const shared_ptr<T, DF>& sp)
		
			if (this != &sp)
			
				// 在和sp共享之前,this先要将之前的状态清空
				Release();

				// this就可以和sp共享资源以及计数了
				_ptr = sp._ptr;
				_pCount = sp._pCount;
				_pMutex = sp._pMutex;
				AddRef();
			

			return *this;
		

	private:
		void AddRef()
		
			if (nullptr == _ptr)
				return;

			_pMutex->lock();
			++(*_pCount);
			_pMutex->unlock();
		

		void Release()
		
			if (nullptr == _ptr)
				return;

			bool isDelete = false;
			_pMutex->lock();

			if (_ptr && 0 == --(*_pCount))
			
				// delete _ptr;
				DF df;
				df(_ptr);
				delete _pCount;
				_pCount = nullptr;
				isDelete = true;
			

			_pMutex->unlock();

			if (isDelete)
			
				delete _pMutex;
			
		
	private:
		T* _ptr;        // 用来接收资源的
		int* _pCount;   // 指向了引用计数的空间---记录的是使用资源的对象的个数
		mutex* _pMutex; // 目的:保证对引用计数的操作是安全的
	;



void TestSharedPtr()

	bite::shared_ptr<int> sp1(new int);
	bite::shared_ptr<int> sp2(sp1);

	bite::shared_ptr<int> sp3(new int);
	bite::shared_ptr<int> sp4(sp3);

	sp3 = sp2;   // sp2和sp3共享同一份资源了

	sp4 = sp2;   // sp2和sp4共享同一份资源了



// 这个语法也是C++11新的语法:在定义成员变量时,可以就地初始化
struct A

	int a = 0;
	int b = 0;
	int c = 0;
;


// 线程函数
void ThreadFunc(bite::shared_ptr<A>& sp, int n)

	for (int i = 0; i < n; ++i)
	
		bite::shared_ptr<A> copy(sp);
		copy->a++;
		copy->b++;
		copy->c++;
	



#include <thread>

void TestSharedPtrSafe()

	bite::shared_ptr<A> sp(new A);

	// t1对象将来就和对应的线程绑定一起了
	thread t1(ThreadFunc, sp, 10000);
	thread t2(ThreadFunc, sp, 10000);

	// 线程等待
	t1.join();
	t2.join();


	// 1. 代码会不会崩溃----->shared_ptr是否安全---没有崩溃:
	// 2. A 对象中的各个成员的值是不是都是20000
	cout << sp->a << endl;
	cout << sp->b << endl;
	cout << sp->c << endl;




/
// 利用标准库中的shared_ptr来进行测试
#include <memory>
void ThreadFuncstd(shared_ptr<A>& sp, int n)

	for (int i = 0; i < n智能指针11

智能指针11

智能指针11

C++11 unique_ptr智能指针详解

C++11 智能指针和多态性

C++11 — 智能指针

(c)2006-2024 SYSTEM All Rights Reserved IT常识