智能指针

一、资源分配即初始化:定义一个类来进行资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放(构造函数负责把资源分配给你,析构函数负责释放资源)

建议:在任何时候都不要使用AutoPtr,除非不用调用拷贝构造、赋值运算符的重载

template<class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr = NULL)//外部用户没有提供,默认给成空
		//构造函数负责分配资源
		:_ptr(ptr)
	{
		cout << "AutoPtr:" << this<<endl;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	//析构函数:把资源拿走
	~AutoPtr()
	{
		cout << "~AutoPtr:" << this << endl;
		if (_ptr)
			delete _ptr;
	}
private:
	T* _ptr;
};
struct A
{
	int _a;
	int _b;
	int _c;
};
void TestAutoPtr()
{
	int *p1 = new int;
	*p1 = 10;
	AutoPtr<int> ap1(new int);//此时的对象不能直接当作一个指针进行解引用
	//使用*或->,需要重载*或->
	*ap1 = 10;
	A* p2 = new A;
	p2->_b = 10;
	AutoPtr<A> ap2(new A);
	ap2->_b = 10;
	delete p1;//在这种方式下,必须手动释放p1和p2
	delete p2;
}
int main()
{
	return 0;
}

这种智能指针的存在,使得函数返回的位置不需要释放空间,不需要关闭指针,调用析构函数处理。

这种结构存在的缺陷:浅拷贝的方式,拷贝构造后,两个对象会指向同一个空间,释放时会出现问题。【没有把拷贝构造函数实现出来,是一个浅拷贝,浅拷贝导致两个对象共用同一块资源,同一块资源被释放多次,出现问题】

void TestAutoPtr()
{
	int *p1 = new int;//先申请一个整型的空间
	int *p11(p1);//让p11也指向这个整型的空间
	*p1 = 10;//把p1的内容改为10
	AutoPtr<int> ap1(new int);//申请一端整型的空间,交给ap1进行管理
	AutoPtr<int> ap11(ap1);//用ap1拷贝构造ap11
	*ap1 = 10;//
	//出了函数的作用域,p1和p2管理的单个的空间已经释放了,接着释放三个类对象
	//销毁时先创建的后销毁:先销毁ap2,再销毁ap11,ap11通过ap1拷贝构造
	//会出现问题:浅拷贝的方式
	A* p2 = new A;
	p2->_b = 10;
	AutoPtr<A> ap2(new A);
	ap2->_b = 10;
	delete p1;//在这种方式下,必须手动释放p1和p2
	delete p2;
}

此时不能采用深拷贝的方式解决,因为,深拷贝要求ap1拥有资源,要求ap11也拥有资源。这块空间是外部用户申请 的空间,不是当前类申请的空间,这个类只是负责管理空间,没有申请空间的权力,只有释放空间的权力。

2、解决方式:ap1拷贝构造ap11,将ap1的管理控制权交给ap11,将ap1中的资源转移到ap11(当前对象)

添加拷贝构造函数:

//拷贝构造函数
	AutoPtr(AutoPtr<T>& ap)
		:_ptr(ap._ptr)//先让ap和ptr共用同一块资源
	{
		//再让ap和ptr脱离关系
		ap._ptr = NULL;
	}

主函数中:

void TestAutoPtr()
{
	int *p1 = new int;//先申请一个整型的空间
	int *p11(p1);//让p11也指向这个整型的空间
	*p1 = 10;//把p1的内容改为10
	AutoPtr<int> ap1(new int);//申请一端整型的空间,交给ap1进行管理
	AutoPtr<int> ap11(ap1);//用ap1拷贝构造ap11
	//*ap1 = 10;此时操纵ap1程序会崩溃,因为ap1已经不存在了
//此时只能操纵ap11
        *ap11=10;
	A* p2 = new A;
	p2->_b = 10;
	AutoPtr<A> ap2(new A);
	ap2->_b = 10;
	delete p1;//在这种方式下,必须手动释放p1和p2
	delete p2;
}

资源的转移:类的对象通过构造实现,若是要调用拷贝构造函数、运算符的重载,必须把参数转移到当前对象上去,但是仍然存在缺陷,ap1和原来的空间完全没有关系了(任何情况下都不要去使用AutoPtr),不熟悉AutoPtr实现原理的人很可能通过ap1去操作,就会出现问题:

template<class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr = NULL)//外部用户没有提供,默认给成空
		//构造函数负责分配资源
		:_ptr(ptr)
	{
		cout << "AutoPtr:" << this<<endl;
	}
	//拷贝构造函数
	//将ap对象中的资源转移到*this(当前对象)
	AutoPtr(AutoPtr<T>& ap)
		:_ptr(ap._ptr)//先让ap和ptr共用同一块资源
	{
		//再让ap和ptr脱离关系
		ap._ptr = NULL;
	}
	//赋值运算符的重载
	AutoPtr<T>& operator=(AutoPtr<T>& ap)
	{
		if (this != &ap)
		{
			if (_ptr)
			//先将当前对象里面管理的资源清理,再接收当前资源里面的资源
			  delete _ptr;
			//把ap的资源转移
			_ptr = ap->_ptr;
			//和ap脱离关系
			ap._ptr = NULL;
		}
		//是自己给自己赋值
		return *this;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	//析构函数:把资源拿走
	~AutoPtr()
	{
		cout << "~AutoPtr:" << this << endl;
		if (_ptr)
			delete _ptr;
	}
private:
	T* _ptr;
};
struct A
{
	int _a;
	int _b;
	int _c;
};
void TestAutoPtr()
{
	int *p1 = new int; 
	int *p11(p1); 
	*p1 = 10; 
	p11 = p1;
	AutoPtr<int> ap1(new int); 
	AutoPtr<int> ap11(ap1); 
	*ap11 = 10;
	AutoPtr<int> ap3(new int);
	ap3 = ap11;//此时会调用赋值运算符的重载
	//此时ap3和ap11共用同一块空间,也会发生浅拷贝
	A* p2 = new A;
	p2->_b = 10;
	AutoPtr<A> ap2(new A);
	ap2->_b = 10;
	delete p1;//在这种方式下,必须手动释放p1和p2
	delete p2;
}

(3)改进会直接和原来空间脱离关系的情况:不让其与原来的空间完全脱离关系。对c++98AutoPtr的改进/优化

原理:资源释放管理权的转移

ap前面加上了一个const:AutoPtr(const AutoPtr<T>& ap),再函数体里面要进行修改当前对象里面的成员变量是不成立的,以下代码调用拷贝构造函数会发生错误:

//对c++98AutoPtr的改进/优化
template<class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr = NULL)
		:_ptr(ptr)
		, _owner(NULL)
	{
		if (_ptr)//当前对象管理资源了
			_owner = true;
	}
	//需要给出拷贝构造和赋值运算符的重载,否则是浅拷贝的方式
	AutoPtr(const AutoPtr<T>& ap)
		:_ptr(ap._ptr)
		, _owner(ap._owner)
	{
		ap._owner = false;
	}
	~AutoPtr()
	{
		if (_owner&&_ptr)
			delete _ptr;
	}
private:
	T* _ptr;
	bool _owner;
};
//调用拷贝构造函数
void TestAutoPtr()
{
	//通过构造函数创建ap1
	AutoPtr<int> ap1(new int);
	//通过ap1拷贝构造ap2
	AutoPtr<int> ap2(ap1);
}

此时,我们偏想让这个代码通过编译,可进行以下操作:加上关键字mutable

private:
	T* _ptr;
	mutable bool _owner;

即使是const类型的变量也是可以修改的

此时实现了一个对象拷贝另一个对象,两个对象都可以操作空间,但是只有当前对象对空间有释放的权力

template<class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr = NULL)
		:_ptr(ptr)
		, _owner(NULL)
	{
		if (_ptr)//当前对象管理资源了
			_owner = true;
	}
	//需要给出拷贝构造和赋值运算符的重载,否则是浅拷贝的方式
	AutoPtr(const AutoPtr<T>& ap)
		:_ptr(ap._ptr)
		, _owner(ap._owner)
	{
		ap._owner = false;
	}
	//重载符号
	T& operator*()
	{
		return *_ptr;
	}
	~AutoPtr()
	{
		if (_owner&&_ptr)
			delete _ptr;
	}
private:
	T* _ptr;
	mutable bool _owner;
};
//调用拷贝构造函数
void TestAutoPtr()
{
	//通过构造函数创建ap1
	AutoPtr<int> ap1(new int);
	//通过ap1拷贝构造ap2
	AutoPtr<int> ap2(ap1);
	*ap1 = 20;
	*ap2 = 55;
}

4、在实现拷贝构造的基础上实现运算符的重载

template<class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr = NULL)
		:_ptr(ptr)
		, _owner(NULL)
	{
		if (_ptr)//当前对象管理资源了
			_owner = true;
	}
	//需要给出拷贝构造和赋值运算符的重载,否则是浅拷贝的方式
	AutoPtr(const AutoPtr<T>& ap)
		:_ptr(ap._ptr)
		, _owner(ap._owner)
	{
		ap._owner = false;
	}
	//赋值运算符的重载
	AutoPtr<T>& operator=(const AutoPtr<T>& ap)
	{
		if (this != &ap)
		{
			if (_owner&&_ptr)
				delete _ptr;
			//让ap接收当前对象里面的资源
			_ptr = ap._ptr;
			_owner = ap._owner;
			//ap里面的资源没有释放的权力,将owner改为false
			ap._owner = false;
		}
		return *this;
	}
	//重载符号
	T& operator*()
	{
		return *_ptr;
	}
	~AutoPtr()
	{
		if (_owner&&_ptr)
			delete _ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;
	mutable bool _owner;
};
//调用拷贝构造函数
void TestAutoPtr()
{
	//通过构造函数创建ap1
	AutoPtr<int> ap1(new int);
	//通过ap1拷贝构造ap2
	AutoPtr<int> ap2(ap1);
	AutoPtr<int> ap3;

	*ap1 = 20;
	*ap2 = 55;
	ap3 = ap2;
	*ap3 = 90;
}
int main()
{
	TestAutoPtr();
	system("pause");
	return 0;
}

这两个版本AutoPtr,第二个版本中存在一个更加严重的缺陷,下述代码会发生问题,使代码崩溃:

void TestAutoPtr()
{
	//通过构造函数创建ap1
	AutoPtr<int> ap1(new int);
	//通过ap1拷贝构造ap2
	AutoPtr<int> ap2(ap1);
 
	*ap1 = 20;
	*ap2 = 55;
	if (true)
	{
		AutoPtr<int> ap3;
		ap3 = ap2;
		*ap3 = 90;
	}
	*ap2 = 88;
}

ap3出了作用域对代码进行销毁,但是ap1和ap2不知道,ap1、ap2成为了野指针,导致程序崩溃。

继续进行改进,将版本恢复到第一个版本

c++中boost库实现智能指针

二、为了解决autoPtr中的问题而引入的一部分智能指针

1、scoped_ptr:解决调用拷贝构造,或者用一个对象给另外一个对象赋值的问题,只能管理单个的空间(一个类的对象最多只能拥有一个资源)

(1)先将拷贝构造函数和赋值运算符的重载给成私有的,则不能调用

//让拷贝构造函数和赋值运算符的重载函数不能调用
template<class>
class ScopedPtr
{
public:
	ScopedPtr(T* ptr = NULL)
		:_ptr(ptr)
	{}
	~ScopedPtr()
	{
		if (_ptr)
			delete _ptr;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;//管理空间的地址
	}
private:
	//必须给出拷贝构造函数,不然会使用默认的拷贝构造
	//将函数给成私有的,则不能调用此函数
	ScopedPtr(const ScopedPtr<T>& sp)
	{}
	ScopedPtr<T>& operator=(const ScopedPtr<T>& sp)
	{
		return *this;
	}
private:
	T* _ptr;//管理函数的指针
};
void TestScopedPtr()
{
	ScopedPtr<int> sp1(new int);
	//ScopedPtr<int> sp2(sp1);//用sp1拷贝构造sp2会失败
	//ScopedPtr<int> sp2;//此函数中什么都不给,会构成一个空的地址
	//sp2 = sp1;赋值操作也会失败,因为赋值运算符的重载是私有的
}

但是如果使用友元函数,则是可以调用私有的函数的

class ScopedPtr
{
	friend void TestScopedPtr();
public:

(2)给拷贝构造函数和赋值运算符的重载只给出声明,不给定义。将这两个函数给出来,在编译的期间可以执行,但是在链接的时候会找不到

template<class>
class ScopedPtr
{
 public:
	ScopedPtr(T* ptr = NULL)
		:_ptr(ptr)
	{}
	~ScopedPtr()
	{
		if (_ptr)
			delete _ptr;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;//管理空间的地址
	}
	ScopedPtr(const ScopedPtr<T>& sp);
	ScopedPtr<T>& operator=(const ScopedPtr<T>& sp);
private:
	T* _ptr;//管理函数的指针
};
void TestScopedPtr()
{
	ScopedPtr<int> sp1(new int);
}

权限是公有的,用户进行了定义,也是可以调用的

template<class>
class ScopedPtr
{
 public:
	ScopedPtr(T* ptr = NULL)
		:_ptr(ptr)
	{}
	~ScopedPtr()
	{
		if (_ptr)
			delete _ptr;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;//管理空间的地址
	}
	ScopedPtr(const ScopedPtr<T>& sp);
	ScopedPtr<T>& operator=(const ScopedPtr<T>& sp);
private:
	T* _ptr;//管理函数的指针
};
template<class T>
ScopedPtr<T>& ScopedPtr<T>::operator = (const ScopedPtr<T>& sp)
{
	return *this;
}
void TestScopedPtr()
{
	ScopedPtr<int> sp1(new int);
}

将这两种方式结合起来即可:为了防止用户在外部调用,将只给出的函数的声明写在private作用域内

template<class>
class ScopedPtr
{
 public:
	ScopedPtr(T* ptr = NULL)
		:_ptr(ptr)
	{}
	~ScopedPtr()
	{
		if (_ptr)
			delete _ptr;
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;//管理空间的地址
	}
private:
	ScopedPtr(const ScopedPtr<T>& sp);
	ScopedPtr<T>& operator=(const ScopedPtr<T>& sp);
private:
	T* _ptr;//管理函数的指针
};
template<class T>
ScopedPtr<T>& ScopedPtr<T>::operator = (const ScopedPtr<T>& sp)
{
	return *this;
}
void TestScopedPtr()
{
	ScopedPtr<int> sp1(new int);
}

(3)在C++11中,可以不给成私有的,在声明后写上等于delete

ScopedPtr(const ScopedPtr<T>& sp)=delete;
	ScopedPtr<T>& operator=(const ScopedPtr<T>& sp)=delete;

2、scope_array:管理连续的空间,防止被拷贝

template<class T>
class ScopedArray
{
public:
	ScopedArray(T* ptr=NULL)
		:_ptr(ptr)
	{}
	~ScopedArray()
	{
		if (_ptr)
			delete[] _ptr;
	}
	//重载下标运算符
	T& operator[](size_t index)
	{
		return _ptr[index];
	}
	const T& operator[](size_t index)const
	{
		return _ptr[index];
	}
private:
	ScopedArray(ScopedArray<T>&);
	ScopedArray<T>& operator=(ScopedArray<T>&);
private:
	T* _ptr;
};

标准库中引入了ScopedPtr,但是没有ScopedArray,因为库中有vector且类型更加齐全。所有智能指针的头文件都包含在"#include<memory>中。标准库中存在unique_ptr:先调用operator new将空间申请出来;成功后调用构造函数【实现方式也是只给声明不给定义】

#include<memory>
int main()
{
	unique_ptr<int> up1(new int);
	//unique_ptr<int> up2(up1);不能进行拷贝
	//unique_ptr<int> up2;
	//up2 = up1;//不能赋值
 	return 0;
}

3、shared_array:

4、shared_ptr:引用计数

 

 

 

一、智能指针:

1、智能指针给出的原因

 

2、智能指针原理

RAII:在构造函数中分配资源,在析构函数中释放资源,可以进行解引用或者通过->访问空间中的结构体变量。需要重载这几个符号。(迭代器也是把指针进行了封装、也进行了解引用、也进行了重载,所以迭代器也可以成为一种类型的智能指针)

3、了解哪些智能指针

迭代器也是把指针进行了封装、也进行了解引用、也进行了重载,所以迭代器也可以成为一种类型的智能指针

二、shared_ptr

1、ScopedPtr和sharedPtr在C++11中被加到了标准库中,原理相同。ScopedPtr是boost库里面的,boost库是c++中非常重要的一个库,和标准库一样提供了一些非常重要的功能供大家调用,用到时候也是免费的。(当你在不是很了解boost库里面详细的内容的时候,不要轻易说你了解ScopedPtr,因为你不了解其详细内容)在标准库中unique_ptr和其具有相同的功能,在unique_ptr管理资源只能管理一块资源,只能被一个智能指针的资源进行管理,不能几个指针进行共享。其两种实现方式在上一篇关于智能指针的博客中有讲到.

 

template<class T>
class SharedPtr
{
public:
	//构造函数
	SharedPtr(T* ptr = NULL)//没有传任何实参时,缺省值是空的
		:_ptr(ptr)//赋值给当前对象
		, _pCount(NULL)
	{
		if (_ptr)//如果不为空(即外部实参已经将资源给出来了)
		{
			_pCount = new int(1);//新申请一端空间,将空间中的内容给成1
			//给成1是因为外部用户将资源给进来之后,当前只有一个对象进行管理
		}
	}
	//拷贝构造
	SharedPtr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr)//共用同一块空间
		, _pCount(sp._pCount)//共用同一块引用计数
	{
		//智能给当前对象的引用计数加1
		//当前对象如果存在,给引用计数++
		if (_pCount)
		{
			++(*_pCount);
		}
		//_pCount && ++(*_pCount);//不使用循环和if判断实现上述方式
	}

	//赋值运算符的重载:sp1=sp2
	//可能会出现的情况:a、sp1为NULL;b、sp1管理了一段空间,但是这段空间
	//只是sp1一个对象在进行管理(资源独占)[应释放sp1的空间,
	//在和sp2共用同一块空间,引用计数++】;
	//c、sp1资源共享,【应将引用计数--】
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (this!=&sp)
		{
			//当前对象管理资源了,_pCount一定不为空
			//0==--(*_pCount说明当前对象自己独立管理资源
			if (_pCount&&0==--(*_pCount)
			{
				delete _ptr;
				delete _pCount;
			}
			//当前对象在里面没有管理资源,但是也会共享同一块引用计数
			_ptr = sp._ptr;
			_pCount = sp._pCount;
			if (_pCount)
				++(*_pCount);//共享同一块空间需要将引用计数++
		}
		return *this;
	}
	//析构函数
	~SharedPtr()
	{
		if (_pCount && 0 == --(*_pCount))//当前对象独立管理资源
		{
			delete _ptr;
			delete _pCount;
		}
	}
T& operator*()
	{
		return (*_ptr);
	}
	T& operator->()
	{
		return _ptr;
	}
	//
	int UseCount()const
	{
		return (*_pCount);
	}
private:
	T* _ptr;
	int *_pCount;//计数空间
};
void TestShared()
//sp1未管理资源
{
	SharedPtr<int> sp1;
	SharedPtr<int> sp2(new int);
	cout << sp2.UseCount() << endl;
	sp1 = sp2;
	cout << sp2.UseCount() << endl;
}
void TestShared1()
//测试独立管理资源
{
	SharedPtr<int> sp1(new int);
	SharedPtr<int> sp2(new int);
	cout << sp2.UseCount() << endl;
	sp1 = sp2;
	cout << sp2.UseCount() << endl;
}
void TestShared2()
//sp1共享资源
{
	SharedPtr<int> sp1(new int);
	SharedPtr<int> sp2(new int);
	cout << sp2.UseCount() << endl;
	SharedPtr<int> sp3(sp1);//sp1和sp3共享空间
	sp1 = sp2;
	cout << sp2.UseCount() << endl;
}
int main()
{
	TestShared();
	return 0;
}

拷贝构造函数的思路:

存在问题,若是使用如下测试代码,程序会出错:因为析构函数里面直接对函数进行了delete操作,而此时我们创建新空间使用的是malloc

void TestShared3()
 {
	SharedPtr<int> sp1((int*)malloc(sizeof(int)));
	SharedPtr<FILE> sp2(fopen("1.txt","rb"));//rb按照只读二进制的形式打开
 	SharedPtr<int> sp3(new int);//sp1和sp3共享空间
}

 不同的指针应该有不同的方式去处理指针的清理工作,

解决方式1:定制一个删除器:

template<class T>
void DeletePtr(T*& ptr)
{
	if (ptr)
	{
		delete ptr;
		ptr = NULL;
	}
 }
//对于malloc申请的空间
template<class T>
void FreePtr(T*& ptr)
{
	if (ptr)
	{
		free(ptr);
		ptr = NULL;
	}
}
//文件指针
void FClosedPtr(FILE*& ptr)
{
	if (ptr)
	{
		fclose(ptr);
		ptr = NULL;
	}
}

//给出函数指针
template<class T>
class _PDF
{
public:
	typedef void(*PDF)(T*&);//定义一个函数指针的类型
};


template<class T>
class SharedPtr
{
	typename typedef __PDF<T>::PDF PDF;
	//typename相当于告诉编译器PDF是一个类型,不是静态成员变量
public:
	//构造函数
	SharedPtr(T* ptr = NULL,PDF pdf=DeletePtr)//没有传任何实参时,缺省值是空的
		:_ptr(ptr)//赋值给当前对象
		, _pCount(NULL)
                ,_pDf(pdf)
	{
		if (_ptr)//如果不为空(即外部实参已经将资源给出来了)
		{
			_pCount = new int(1);//新申请一端空间,将空间中的内容给成1
			//给成1是因为外部用户将资源给进来之后,当前只有一个对象进行管理
		}
	}
	//拷贝构造
	SharedPtr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr)//共用同一块空间
		, _pCount(sp._pCount)//共用同一块引用计数
	{
		//智能给当前对象的引用计数加1
		//当前对象如果存在,给引用计数++
		if (_pCount)
		{
			++(*_pCount);
		}
		//_pCount && ++(*_pCount);//不使用循环和if判断实现上述方式
	}

	//赋值运算符的重载:sp1=sp2
	//可能会出现的情况:a、sp1为NULL;b、sp1管理了一段空间,但是这段空间
	//只是sp1一个对象在进行管理(资源独占)[应释放sp1的空间,
	//在和sp2共用同一块空间,引用计数++】;
	//c、sp1资源共享,【应将引用计数--】
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (this!=&sp)
		{
			//当前对象管理资源了,_pCount一定不为空
			//0==--(*_pCount说明当前对象自己独立管理资源
			if (_pCount&&0==--(*_pCount)
			{
				//delete _ptr;
                                _pDf(_ptr);
				delete _pCount;
			}
			//当前对象在里面没有管理资源,但是也会共享同一块引用计数
			_ptr = sp._ptr;
			_pCount = sp._pCount;
			if (_pCount)
				++(*_pCount);//共享同一块空间需要将引用计数++
		}
		return *this;
	}
	//析构函数
	~SharedPtr()
	{
		if (_pCount && 0 == --(*_pCount))//当前对象独立管理资源
		{
			//delete _ptr;
                        _pDf(_ptr);
			delete _pCount;
		}
	}
	T& operator*()
	{
		return (*_ptr);
	}
	T& operator->()
	{
		return _ptr;
	}
	//
	int UseCount()const
	{
		return (*_pCount);
	}
private:
	T* _ptr;
	int *_pCount;//计数空间
        PDF _pDF;
};
void TestShared()
//sp1未管理资源
{
	SharedPtr<int> sp1;
	SharedPtr<int> sp2(new int);
	cout << sp2.UseCount() << endl;
	sp1 = sp2;
	cout << sp2.UseCount() << endl;
}
void TestShared1()
//测试独立管理资源
{
	SharedPtr<int> sp1(new int);
	SharedPtr<int> sp2(new int);
	cout << sp2.UseCount() << endl;
	sp1 = sp2;
	cout << sp2.UseCount() << endl;
}
void TestShared2()
//sp1共享资源
{
	SharedPtr<int> sp1(new int);
	SharedPtr<int> sp2(new int);
	cout << sp2.UseCount() << endl;
	SharedPtr<int> sp3(sp1);//sp1和sp3共享空间
	sp1 = sp2;
	cout << sp2.UseCount() << endl;
}
void TestShared3()
 {
	SharedPtr<int> sp1((int*)malloc(sizeof(int)),FreePtr);
	SharedPtr<int> sp2(fopen("1.txt","rb"),FClosedPtr);//rb按照只读二进制的形式打开
 	SharedPtr<int> sp3(new int);//sp1和sp3共享空间
}
int main()
{
	TestShared3();
	return 0;
}

解决方式2:加上一个模板类型的参数

//函数参数
//相当于把Dx创建的无名对象像函数一样使用(即仿函数):
//只需要在类里面重载()
template<class T>
class Delete
{
public:
	//重载()
	void operator()(T*& ptr)
	{
		if (ptr)
		{
			delete ptr;
			ptr = NULL;
		}
	}
};
template<class T>
class Free
{
public:
	void operator()(T*& ptr)
	{
		if (ptr)
		{
			free(ptr);
			ptr=NULL:
		}
	}
};
//文件指针的类,本身就是FILE*,不需要给成模板类
class FClose
{
public:
	void operator()(FILE*& ptr)
	{
		if (ptr){
			fclose(ptr);
			ptr = NULL;
		}
	}
};
template<class T,class Dx=Delete<T>>//此时Dx是一个类型
class SharedPtr
{
public:
	//构造函数
	SharedPtr(T* ptr = NULL)//没有传任何实参时,缺省值是空的
		:_ptr(ptr)//赋值给当前对象
		, _pCount(NULL)
	{
		if (_ptr)//如果不为空(即外部实参已经将资源给出来了)
		{
			_pCount = new int(1);//新申请一端空间,将空间中的内容给成1
			//给成1是因为外部用户将资源给进来之后,当前只有一个对象进行管理
		}
	}
	//拷贝构造
	SharedPtr(const SharedPtr<T>& sp)
		:_ptr(sp._ptr)//共用同一块空间
		, _pCount(sp._pCount)//共用同一块引用计数
	{
		//智能给当前对象的引用计数加1
		//当前对象如果存在,给引用计数++
		if (_pCount)
		{
			++(*_pCount);
		}
		//_pCount && ++(*_pCount);//不使用循环和if判断实现上述方式
	}

	//赋值运算符的重载:sp1=sp2
	//可能会出现的情况:a、sp1为NULL;b、sp1管理了一段空间,但是这段空间
	//只是sp1一个对象在进行管理(资源独占)[应释放sp1的空间,
	//在和sp2共用同一块空间,引用计数++】;
	//c、sp1资源共享,【应将引用计数--】
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (this!=&sp)
		{
			//当前对象管理资源了,_pCount一定不为空
			//0==--(*_pCount说明当前对象自己独立管理资源
			if (_pCount&&0==--(*_pCount)
			{
				Dx()(_ptr);//直接加上()相当于创建了一个对象
				//类似于一个函数对象
				delete _pCount;
			}
			//当前对象在里面没有管理资源,但是也会共享同一块引用计数
			_ptr = sp._ptr;
			_pCount = sp._pCount;
			if (_pCount)
				++(*_pCount);//共享同一块空间需要将引用计数++
		}
		return *this;
	}
	//析构函数
	~SharedPtr()
	{
		if (_pCount && 0 == --(*_pCount))//当前对象独立管理资源
		{
			Dx()(_ptr);//直接加上()相当于创建了一个对象
				//类似于一个函数对象
			delete _pCount;
		}
	}
	T& operator*()
	{
		return (*_ptr);
	}
	T& operator->()
	{
		return _ptr;
	}
	//
	int UseCount()const
	{
		return (*_pCount);
	}
private:
	T* _ptr;
	int *_pCount;//计数空间
};
void TestShared()
{
	SharedPtr<int,Free<int>> sp1((int*)malloc(sizeof(int)));
	SharedPtr<FILE,FClose> sp2(fopen("1.txt", "rb"));//rb按照只读二进制的形式打开
	SharedPtr<int> sp3(new int);//sp1和sp3共享空间
}

int main()
{
	TestShared();
	return 0;
}

3、使用标准库中的shared_ptr处理问题:

int main()
{
	shared_ptr<int> sp1(new int);//管理单个的空间
	cout << sp1.use_count() << endl;
	//sp1拷贝构造sp2
	shared_ptr<int> sp2(sp1);
	cout << sp1.use_count() << endl;
	return 0;
}

4、shared_ptr自身有一个很大的缺陷,有可能存在循环引用的问题,没有调用析构函数,会发生内存泄漏,如下述程序:

# include"memory"
template<class T>
struct ListNode
{
	//构造函数
	ListNode(const T& data)
	:_pPre(NULL)
	, _pNext(NULL)
	, _data(data)
	{
		cout << "ListNode(const T&):" << this << endl;
	}
	~ListNode()
	{
		cout << "~ListNode():" << this<<endl;
	}
	//ListNode<T>* _pPre;
	//ListNode<T>* _pNext;
	shared_ptr<ListNode<T>> _pPre;
	shared_ptr < ListNode<T>> _pNext;
	T _data;
};
void TestSharedPtr()
{
	//ListNode<int>* p=new ListNode<T> (10);
	shared_ptr<ListNode<int>> sp1(new ListNode<int> (10));
	shared_ptr<ListNode<int>> sp2(new ListNode<int>(20));
	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;
	//将两个节点链接起来
	sp1->_pNext = sp2;
	sp2->_pPre = sp1;
	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;
}
int main()
{
	TestSharedPtr();
	system("pause");
	return 0;
}

结果为2,没有进行析构、释放

5、解决循环引用的问题:weak_ptr,weak_ptr不能独立的管理空间。和shared_ptr一起使用,解决循环引用的问题,此时会调用析构函数【循环引用的场景,面试重要程度:5颗星】

# include"memory"
template<class T>
struct ListNode
{
	//构造函数
	ListNode(const T& data)
	:_data(data)
	{
		cout << "ListNode(const T&):" << this << endl;
	}
	~ListNode()
	{
		cout << "~ListNode():" << this<<endl;
	}
	//ListNode<T>* _pPre;
	//ListNode<T>* _pNext;
	weak_ptr<ListNode<T>> _pPre;
	weak_ptr < ListNode<T>> _pNext;
	T _data;
};
void TestSharedPtr()
{
	//weak_ptr<int> wp(new int);//不能单独使用,必须依附在shared_ptr之上
	//ListNode<int>* p=new ListNode<T> (10);
	shared_ptr<ListNode<int>> sp1(new ListNode<int> (10));
	shared_ptr<ListNode<int>> sp2(new ListNode<int>(20));
	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;
	//将两个节点链接起来
	sp1->_pNext = sp2;
	sp2->_pPre = sp1;
	cout << sp1.use_count() << endl;
	cout << sp2.use_count() << endl;
}
int main()
{
	TestSharedPtr();
	system("pause");
	return 0;
}

结果:

 

posted on 2018-08-23 14:48  众里寻他2018  阅读(67)  评论(0)    收藏  举报

导航