C++智能指针

1.为什么需要智能指针?

2. 内存泄漏

3.智能指针的使用及原理

4.C++11boost中智能指针的关系

5.RAII扩展学习

1. 为什么需要智能指针?

下面我们先分析一下下面这段程序有没有什么 内存方面 的问题?提示一下:注意分析 MergeSort 函数中的问题。

#include <vector>
void _MergeSort(int* a, int left, int right, int* tmp) {
	if (left >= right) return;

	int mid = left + ((right - left) >> 1);
	// [left, mid]
	// [mid+1, right]
	_MergeSort(a, left, mid, tmp);
	_MergeSort(a, mid + 1, right, tmp);
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	int index = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
			tmp[index++] = a[begin1++];
		else
			tmp[index++] = a[begin2++];
	}
	while (begin1 <= end1)
		tmp[index++] = a[begin1++];
	while (begin2 <= end2)
		tmp[index++] = a[begin2++];
	memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));
}
void MergeSort(int* a, int n) {
	int* tmp = (int*)malloc(sizeof(int) * n);
	_MergeSort(a, 0, n - 1, tmp);

	// 这里假设处理了一些其他逻辑
	vector<int> v(1000000000, 10);
	// ...

	// free(tmp);
}
int main()
{
	int a[5] = { 4, 5, 2, 3, 1 };
	MergeSort(a, 5);
	return 0;
}
问题分析:上面的问题分析出来我们发现有以下两个问题?
1. malloc出来的空间,没有进行释放,存在内存泄漏的问题。
2. 异常安全问题。如果在mallocfree之间如果存在抛异常,那么还是有内存泄漏。这种问题就叫异常安全。

2. 内存泄漏

2.1 什么是内存泄漏,内存泄漏的危害

什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不 是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的制,因而 造成了内存的浪费。 内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。
void MemoryLeaks()
 {
 // 1.内存申请了忘记释放
 int* p1 = (int*)malloc(sizeof(int));
 int* p2 = new int;
 
 // 2.异常安全问题
 int* p3 = new int[10];
 
 Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
 
 delete[] p3;
 }
2.2 内存泄漏分类(了解)
C/C++ 程序中一般我们关心两种方面的内存泄漏:
堆内存泄漏 (Heap leak)
堆内存指的是程序执行中依据须要分配通过 malloc / calloc / realloc / new 等从堆中分配的一块内存,用完后必须通过调用相应的 free 或者 delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那 么以后这部分空间将无法再被使用,就会产生Heap Leak
系统资源泄漏
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定
2.3 如何检测内存泄漏(了解)
linux 下内存泄漏检测: linux下几款内存泄漏检测工具
windows 下使用第三方工具: VLD工具说明

其他工具:内存泄漏工具比较

2.4如何避免内存泄漏

1. 工程前期良好的设计规范,养成良好的编码规范,申请的内存空间记着匹配的去释放。 ps :这个理想状 态。但是如果碰上异常时,就算注意释放了,还是可能会出问题。需要下一条智能指针来管理才有保 证。
2. 采用 RAII 思想或者智能指针来管理资源。
3. 有些公司内部规范使用内部实现的私有内存管理库。这套库自带内存泄漏检测的功能选项。
4. 出问题了使用内存泄漏工具检测。 ps :不过很多工具都不够靠谱,或者收费昂贵。
总结一下 :
内存泄漏非常常见,解决方案分为两种: 1 、事前预防型。如智能指针等。 2 、事后查错型。如泄漏检测工具。

3.智能指针的使用及原理

3.1 RAII

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

 不需要显式地释放资源。 采用这种方式,对象所需的资源在其生命期内始终保持有效。

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

	}
	~SmartPtr()
	{
		delete _ptr;
		_ptr = nullptr;
	}
private:
	T* _ptr;
};
int main()
{
	SmartPtr<int> sp(new int);
}
总结一下智能指针的原理:
1. RAII 特性
2. 重载 operator* opertaor-> ,具有像指针一样的行为。

C++98引入了一个智能指针auto_ptr,代码如下。

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

		}
		auto_ptr(auto_ptr<T>& sp)   //管理权转移
		{
			_ptr = sp._ptr;
			sp._ptr = nullptr;
		}
		T& operator*()
		{
			return *_ptr;
		} 
		T* operator->()
		{
			return _ptr;
		}
		~auto_ptr()
		{
			if (_ptr)
			{
				cout << "delete" << &_ptr << endl;
				delete _ptr;
			}

		}
	private:
		T* _ptr;
	};

}

int main()
{
	MyPtr::auto_ptr<int> sp1(new int);
	MyPtr::auto_ptr<int> sp2(sp1);

	cout << *sp1 << endl;

}

这里可以看出auto_ptr其实是一个失败的设计,好多公司都禁止使用这个智能指针。他把之前拷贝对象的指针悬空了。这个指针失效了。

unique_ptr  

C++11 中开始提供更靠谱的 unique_ptr
int main()
{
 unique_ptr<Date> up(new Date);
 
 // unique_ptr的设计思路非常的粗暴-防拷贝,也就是不让拷贝和赋值。
 unique_ptr<Date> copy(ap);
 return 0; }
// 模拟实现一份简答的UniquePtr,了解原理
template<class T>
class UniquePtr
{
public:
 UniquePtr(T * ptr = nullptr) 
 : _ptr(ptr)
 {}
 ~UniquePtr() 
 {
if(_ptr)
 delete _ptr;
 }
 T& operator*() {return *_ptr;}
 T* operator->() {return _ptr;}
 
private:
 // C++98防拷贝的方式:只声明不实现+声明成私有
 UniquePtr(UniquePtr<T> const &);
 UniquePtr & operator=(UniquePtr<T> const &);
 
 // C++11防拷贝的方式:delete
 UniquePtr(UniquePtr<T> const &) = delete;
 UniquePtr & operator=(UniquePtr<T> const &) = delete;
 
private:
 T * _ptr;
};

shared_ptr

C++11中开始提供更靠谱的并且支持拷贝的shared_ptr

int main()
{
 // shared_ptr通过引用计数支持智能指针对象的拷贝
 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; }

shared_ptr 的原理:是通过引用计数的方式来实现多个 shared_ptr 对象之间共享资源 。例如:老师晚上在下班之前都会通知,让最后走的学生记得把门锁下。
1. shared_ptr 在其内部, 给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享
2. 对象被销毁时 ( 也就是析构函数调用 ) ,就说明自己不使用该资源了,对象的引用计数减一。
3. 如果引用计数是 0 ,就说明自己是最后一个使用该资源的对象, 必须释放该资源
4. 如果不是 0 ,就说明除了自己还有其他对象在使用该份资源, 不能释放该资源 ,否则其他对象就成野指针了

 那么怎么解决计数的问题,有人就想出一个在静态区开辟一个计数,因为静态区的数据是所有对象共有的。代码如下。

namespace MyPtr
{
	template<class T>
	class share_ptr
	{
	public:
		share_ptr(T* ptr)
			:_ptr(ptr)
		{
			_pCount = 1;
		}
		share_ptr(const share_ptr<T>& sp)
			:_ptr(sp._ptr)
		{
			++_pCount;
		}
		share_ptr<T>& operator=(const share_ptr<T>& sp)
		{
			_ptr = sp._ptr;
			++_pCount;
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		~share_ptr()
		{
			if (--_pCount == 0 && _ptr)
			{
				cout << "delete:" << &_ptr << endl;
				delete _ptr;
			}
		}
	private:
		T* _ptr;
		static int _pCount;
	};
	template<class T>
	int share_ptr<T>::_pCount = 0;
}
int main()
{
	MyPtr::share_ptr<int> sp1(new int);
	MyPtr::share_ptr<int> sp2(sp1);
	MyPtr::share_ptr<int> sp3(sp1);
	//上面3个指针管理同一块资源,引用计数为3

	MyPtr::share_ptr<int> sp4(new int);
	//下面3个指针重新申请是把引用计数修改为1,明显错误
}

这说明在静态区开辟引用计数行不通,那么只有在堆上开辟空间。shared_ptr的线程安全问题

void SharePtrFunc(std::shared_ptr<Date>& sp, size_t n)
{
	//cout << sp.Get() << endl;
	for (size_t i = 0; i < n; ++i)
	{
		// 这里智能指针拷贝会++计数,智能指针析构会--计数,这里是线程安全的。
		std::shared_ptr<Date> copy(sp);
		// 这里智能指针访问管理的资源,不是线程安全的。所以我们看看这些值两个线程++了2n次,但
		//是最终看到的结果,并一定是加了2n
		copy->_year++;
		copy->_month++;
		copy->_day++;
	}
}
int main()
{
	std::shared_ptr<Date> p(new Date);
	//cout << p.Get() << endl;

	const size_t n = 10000;
	thread t1(SharePtrFunc, std::ref(p), n);
	thread t2(SharePtrFunc, std::ref(p), n);
	t1.join();
	t2.join();

	cout << p->_year << endl;
	cout << p->_month << endl;
	cout << p->_day << endl;
	cout << p.use_count() << endl;
	return 0;
}

库里面的是一定保证线程安全的

 可以看出线程安全的引用计数一定是1。

#include<memory>
#include<thread>
#include<mutex>

class Date
{
public:
	int _year = 0;
	int _month = 0;
	int _day = 0;
};
namespace MyPtr
{
	template<class T>
	class share_ptr
	{
	public:
		share_ptr(T* ptr)
			:_ptr(ptr)
			,_pCount(new int(1))
		{

		}
		share_ptr(const share_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pCount(sp._pCount)
		{
			++(*_pCount);
		}
		share_ptr<T>& operator=(const share_ptr<T>& sp)
		{
			//if(this != &sp)
			if (_ptr != sp._ptr) //要用原始指针判断,用类判断自己给自己赋值有问题
			{
				//if (--(*_pCount) == 0 && _ptr)   //要释放以前的资源
				//{
				//	cout << "delete:" << &_ptr <<endl;
				//	delete _ptr;
				//	delete _pCount;
				//}
				Release();
				_ptr = sp._ptr;
				_pCount = sp._pCount;
				//++(*_pCount);
				AddRef();
			}
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		void Release()
		{
			if (--(*_pCount) == 0 && _ptr)   //要释放以前的资源
			{
				delete _ptr;
				delete _pCount;
			}
		}
		int use_count()
		{
			return *_pCount;
		}
		void AddRef()
		{
			++(*_pCount);
		}
		~share_ptr()
		{
			if (--(*_pCount) == 0 && _ptr)
			{
				cout << "delete:" << &_ptr << endl;
				delete _ptr;
			}
		}
	private:
		T* _ptr;
		int *_pCount;  
	};
}
//share_ptr是线程安全的,指在share_ptr内部引用计数是线程安全的,在外面线程安全是需要程序员自己手动保证
//线程安全的,所以在实现share_ptr是需要保证线程安全
void SharePtrFunc(MyPtr::share_ptr<Date>& sp, size_t n)
{
	//cout << sp.Get() << endl;
	for (size_t i = 0; i < n; ++i)
	{
		// 这里智能指针拷贝会++计数,智能指针析构会--计数,这里是线程安全的。
		MyPtr::share_ptr<Date> copy(sp);
		// 这里智能指针访问管理的资源,不是线程安全的。所以我们看看这些值两个线程++了2n次,但
		//是最终看到的结果,并一定是加了2n
		copy->_year++;
		copy->_month++;
		copy->_day++;
	}
}
int main()
{
	MyPtr::share_ptr<Date> p(new Date);
	//cout << p.Get() << endl;

	const size_t n = 10000;
	thread t1(SharePtrFunc, std::ref(p), n);
	thread t2(SharePtrFunc, std::ref(p), n);
	t1.join();
	t2.join();

	cout << p->_year << endl;
	cout << p->_month << endl;
	cout << p->_day << endl;
	cout << p.use_count() << endl;
	return 0;
}

可以看出引用计数不是线程安全的。所以我们需要给引用计数上锁,但是同样需要保证同一块资源看到同一把锁。

#include<memory>
	#include<thread>
	#include<mutex>

	class Date
	{
	public:
		int _year = 0;
		int _month = 0;
		int _day = 0;
	};
	namespace MyPtr
	{
		template<class T>
		class share_ptr
		{
		public:
			share_ptr(T* ptr)
				:_ptr(ptr)
				,_pCount(new int(1))
				,_mtx(new mutex)
			{

			}
			share_ptr(const share_ptr<T>& sp)
				:_ptr(sp._ptr)
				,_pCount(sp._pCount)
			{
				++(*_pCount);
			}
			share_ptr<T>& operator=(const share_ptr<T>& sp)
			{
				//if(this != &sp)
				if (_ptr != sp._ptr) //要用原始指针判断,用类判断自己给自己赋值有问题
				{
					//if (--(*_pCount) == 0 && _ptr)   //要释放以前的资源
					//{
					//	cout << "delete:" << &_ptr <<endl;
					//	delete _ptr;
					//	delete _pCount;
					//}
					Release();
					_ptr = sp._ptr;
					_pCount = sp._pCount;
					//++(*_pCount);
					AddRef();
				}
				return *this;
			}
			T& operator*()
			{
				return *_ptr;
			}
			T* operator->()
			{
				return _ptr;
			}
			void Release()
			{
				_mtx->lock();
				if (--(*_pCount) == 0 && _ptr)   //要释放以前的资源
				{
					delete _ptr;
					delete _pCount;
				}
				_mtx->unlock();
			}
			int use_count()
			{
				return *_pCount;
			}
			void AddRef()
			{
				_mtx->lock();
				++(*_pCount);
				_mtx->unlock();
			}
			~share_ptr()
			{
				if (--(*_pCount) == 0 && _ptr)
				{
					cout << "delete:" << &_ptr << endl;
					delete _ptr;
				}
			}
		private:
			T* _ptr;
			int *_pCount;  
			mutex* _mtx;
		};
	}
	//share_ptr是线程安全的,指在share_ptr内部引用计数是线程安全的,在外面线程安全是需要程序员自己手动保证
	//线程安全的,所以在实现share_ptr是需要保证线程安全
	void SharePtrFunc(MyPtr::share_ptr<Date>& sp, size_t n)
	{
		//cout << sp.Get() << endl;
		for (size_t i = 0; i < n; ++i)
		{
			// 这里智能指针拷贝会++计数,智能指针析构会--计数,这里是线程安全的。
			MyPtr::share_ptr<Date> copy(sp);
			// 这里智能指针访问管理的资源,不是线程安全的。所以我们看看这些值两个线程++了2n次,但
			//是最终看到的结果,并一定是加了2n
			copy->_year++;
			copy->_month++;
			copy->_day++;
		}
	}
	int main()
	{
		MyPtr::share_ptr<Date> p(new Date);
		//cout << p.Get() << endl;

		const size_t n = 10000;
		thread t1(SharePtrFunc, std::ref(p), n);
		thread t2(SharePtrFunc, std::ref(p), n);
		t1.join();
		t2.join();

		cout << p->_year << endl;
		cout << p->_month << endl;
		cout << p->_day << endl;
		cout << p.use_count() << endl;
		return 0;
	}

#include<memory>
#include<thread>
#include<mutex>

class Date
{
public:
	int _year = 0;
	int _month = 0;
	int _day = 0;
};
namespace MyPtr
{
	template<class T>
	class share_ptr
	{
	public:
		share_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			,_pCount(new int(1))
			,_mtx(new mutex)
		{

		}
		share_ptr(const share_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pCount(sp._pCount)
			,_mtx(sp._mtx)
		{
			//++(*_pCount);
			AddRef();
		}
		share_ptr<T>& operator=(const share_ptr<T>& sp)
		{

			//if(this != &sp)
			if (_ptr != sp._ptr) //要用原始指针判断,用类判断自己给自己赋值有问题
			{
				//if (--(*_pCount) == 0 && _ptr)   //要释放以前的资源
				//{
				//	cout << "delete:" << &_ptr <<endl;
				//	delete _ptr;
				//	delete _pCount;
				//}
				Release();
				_ptr = sp._ptr;
				_pCount = sp._pCount;
				_mtx = sp._mtx;
				//++(*_pCount);
				AddRef();
			}
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		void Release()
		{
			_mtx->lock();
			if (--(*_pCount) == 0)   //要释放以前的资源
			{
				cout << "delete:" << &_ptr << endl;
				delete _ptr;
				delete _pCount;
				
			}
			_mtx->unlock();
		}
		int use_count()
		{
			return *_pCount;
		}
		void AddRef()
		{
			_mtx->lock();
			++(*_pCount);
			_mtx->unlock();
		}
		~share_ptr()
		{
			/*if (--(*_pCount) == 0 && _ptr)
			{
				cout << "delete:" << &_ptr << endl;
				delete _ptr;
			}*/
			Release();
		}
	private:
		T* _ptr;
		int *_pCount;  
		mutex* _mtx;
	};
}
//share_ptr是线程安全的,指在share_ptr内部引用计数是线程安全的,在外面线程安全是需要程序员自己手动保证
//线程安全的,所以在实现share_ptr是需要保证线程安全
void SharePtrFunc(MyPtr::share_ptr<Date>& sp, size_t n)
{
	//cout << sp.Get() << endl;
	for (size_t i = 0; i < n; ++i)
	{
		// 这里智能指针拷贝会++计数,智能指针析构会--计数,这里是线程安全的。
		MyPtr::share_ptr<Date> copy(sp);
		// 这里智能指针访问管理的资源,不是线程安全的。所以我们看看这些值两个线程++了2n次,但
		//是最终看到的结果,并一定是加了2n
		copy->_year++;
		copy->_month++;
		copy->_day++;
	}
}
int main()
{
	MyPtr::share_ptr<Date> p(new Date);
	//cout << p.Get() << endl;

	const size_t n = 10000;
	thread t1(SharePtrFunc, std::ref(p), n);
	thread t2(SharePtrFunc, std::ref(p), n);
	t1.join();
	t2.join();
	cout << p->_year << endl;
	cout << p->_month << endl;
	cout << p->_day << endl;

	cout << p.use_count() << endl;

	return 0;
}

加锁后线程是安全的,引用计数一直都1,但是有点小问题是锁是在堆上,还需要释放

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

//
//#include <vector>
//void _MergeSort(int* a, int left, int right, int* tmp) {
//	if (left >= right) return;
//
//	int mid = left + ((right - left) >> 1);
//	// [left, mid]
//	// [mid+1, right]
//	_MergeSort(a, left, mid, tmp);
//	_MergeSort(a, mid + 1, right, tmp);
//	int begin1 = left, end1 = mid;
//	int begin2 = mid + 1, end2 = right;
//	int index = left;
//	while (begin1 <= end1 && begin2 <= end2)
//	{
//		if (a[begin1] < a[begin2])
//			tmp[index++] = a[begin1++];
//		else
//			tmp[index++] = a[begin2++];
//	}
//	while (begin1 <= end1)
//		tmp[index++] = a[begin1++];
//	while (begin2 <= end2)
//		tmp[index++] = a[begin2++];
//	memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));
//}
//void MergeSort(int* a, int n) {
//	int* tmp = (int*)malloc(sizeof(int) * n);
//	_MergeSort(a, 0, n - 1, tmp);
//
//	// 这里假设处理了一些其他逻辑
//	vector<int> v(1000000000, 10);
//	// ...
//
//	// free(tmp);
//}
//int main()
//{
//	int a[5] = { 4, 5, 2, 3, 1 };
//	MergeSort(a, 5);
//	return 0;
//}
// 
// 
//void dev()
//{
//	int x, y;
//	cin >> x >> y;
//	if (y == 0)
//		throw "除0错误";
//}
//int main()
//{
//	int* p1 = new int;
//	try
//	{
//		dev();
//	}
//	catch(exception & e)
//	{
//		cout << "抛异常" << endl;
//	}
//	
//}
//template<class T>
//class SmartPtr
//{
//public:
//	SmartPtr(T* ptr)
//		:_ptr(ptr)
//	{
//
//	}
//	~SmartPtr()
//	{
//		delete _ptr;
//		_ptr = nullptr;
//	}
//private:
//	T* _ptr;
//};
//int main()
//{
//	SmartPtr<int> sp(new int);
//}
//namespace MyPtr
//{
//	template<class T>
//	class auto_ptr
//	{
//	public:
//		auto_ptr(T* ptr)
//			:_ptr(ptr)
//		{
//
//		}
//		auto_ptr(auto_ptr<T>& sp)   //管理权转移
//		{
//			_ptr = sp._ptr;
//			sp._ptr = nullptr;
//		}
//		T& operator*()
//		{
//			return *_ptr;
//		} 
//		T* operator->()
//		{
//			return _ptr;
//		}
//		~auto_ptr()
//		{
//			if (_ptr)
//			{
//				cout << "delete" << &_ptr << endl;
//				delete _ptr;
//			}
//
//		}
//	private:
//		T* _ptr;
//	};
//
//}
//
//int main()
//{
//	/*MyPtr::auto_ptr<int> sp1(new int);
//	MyPtr::auto_ptr<int> sp2(sp1);
//
//	cout << *sp1 << endl;*/
//
//	auto_ptr<int> sp1(new int);
//	auto_ptr<int> sp2(sp1);
//	cout << *sp1 << endl;
//
//}

//
//class Date
//{
//public:
//};
//int main()
//{
//	// shared_ptr通过引用计数支持智能指针对象的拷贝
//	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;
//}


//namespace MyPtr
//{
//	template<class T>
//	class share_ptr
//	{
//	public:
//		share_ptr(T* ptr)
//			:_ptr(ptr)
//		{
//			_pCount = 1;
//		}
//		share_ptr(const share_ptr<T>& sp)
//			:_ptr(sp._ptr)
//		{
//			++_pCount;
//		}
//		share_ptr<T>& operator=(const share_ptr<T>& sp)
//		{
//			_ptr = sp._ptr;
//			++_pCount;
//			return *this;
//		}
//		T& operator*()
//		{
//			return *_ptr;
//		}
//		T* operator->()
//		{
//			return _ptr;
//		}
//		~share_ptr()
//		{
//			if (--_pCount == 0 && _ptr)
//			{
//				cout << "delete:" << &_ptr << endl;
//				delete _ptr;
//			}
//		}
//	private:
//		T* _ptr;
//		static int _pCount;
//	};
//	template<class T>
//	int share_ptr<T>::_pCount = 0;
//}
//int main()
//{
//	MyPtr::share_ptr<int> sp1(new int);
//	MyPtr::share_ptr<int> sp2(sp1);
//	MyPtr::share_ptr<int> sp3(sp1);
//	//上面3个指针管理同一块资源,引用计数为3
//
//	MyPtr::share_ptr<int> sp4(new int);
//	//下面3个指针重新申请是把引用计数修改为1,明显错误
//}

#include<memory>
#include<thread>
#include<mutex>

class Date
{
public:
	int _year = 0;
	int _month = 0;
	int _day = 0;
};
namespace MyPtr
{
	template<class T>
	class share_ptr
	{
	public:
		share_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			,_pCount(new int(1))
			,_mtx(new mutex)
		{

		}
		share_ptr(const share_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pCount(sp._pCount)
			,_mtx(sp._mtx)
		{
			//++(*_pCount);
			AddRef();
		}
		share_ptr<T>& operator=(const share_ptr<T>& sp)
		{

			//if(this != &sp)
			if (_ptr != sp._ptr) //要用原始指针判断,用类判断自己给自己赋值有问题
			{
				//if (--(*_pCount) == 0 && _ptr)   //要释放以前的资源
				//{
				//	cout << "delete:" << &_ptr <<endl;
				//	delete _ptr;
				//	delete _pCount;
				//}
				Release();
				_ptr = sp._ptr;
				_pCount = sp._pCount;
				_mtx = sp._mtx;
				//++(*_pCount);
				AddRef();
			}
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		void Release()
		{
			_mtx->lock();
			bool falg = false;
			if (--(*_pCount) == 0)   //要释放以前的资源
			{
				cout << "delete:" << &_ptr << endl;
				delete _ptr;
				delete _pCount;
				falg = true;
				
			}
			_mtx->unlock();
			if (falg)
				delete _mtx;
		}
		int use_count()
		{
			return *_pCount;
		}
		void AddRef()
		{
			_mtx->lock();
			++(*_pCount);
			_mtx->unlock();
		}
		~share_ptr()
		{
			/*if (--(*_pCount) == 0 && _ptr)
			{
				cout << "delete:" << &_ptr << endl;
				delete _ptr;
			}*/
			Release();
		}
	private:
		T* _ptr;
		int *_pCount;  
		mutex* _mtx;
	};
}
//share_ptr是线程安全的,指在share_ptr内部引用计数是线程安全的,在外面线程安全是需要程序员自己手动保证
//线程安全的,所以在实现share_ptr是需要保证线程安全
void SharePtrFunc(MyPtr::share_ptr<Date>& sp, size_t n)
{
	//cout << sp.Get() << endl;
	for (size_t i = 0; i < n; ++i)
	{
		// 这里智能指针拷贝会++计数,智能指针析构会--计数,这里是线程安全的。
		MyPtr::share_ptr<Date> copy(sp);
		// 这里智能指针访问管理的资源,不是线程安全的。所以我们看看这些值两个线程++了2n次,但
		//是最终看到的结果,并一定是加了2n
		copy->_year++;
		copy->_month++;
		copy->_day++;
	}
}
int main()
{
	MyPtr::share_ptr<Date> p(new Date);
	//cout << p.Get() << endl;

	const size_t n = 10000;
	thread t1(SharePtrFunc, std::ref(p), n);
	thread t2(SharePtrFunc, std::ref(p), n);
	t1.join();
	t2.join();
	cout << p->_year << endl;
	cout << p->_month << endl;
	cout << p->_day << endl;

	cout << p.use_count() << endl;

	return 0;
}

但是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; }
循环引用分析:
1. node1 node2 两个智能指针对象指向两个节点,引用计数变成 1 ,我们不需要手动 delete
2. node1 _next 指向 node2 node2 _prev 指向 node1 ,引用计数变成 2
3. node1 node2 析构,引用计数减到 1 ,但是 _next 还指向下一个节点。但是 _prev 还指向上一个节点。
4. 也就是说 _next 析构了, node2 就释放了。
5. 也就是说 _prev 析构了, node1 就释放了。
6. 但是 _next 属于 node 的成员, node1 释放了, _next 才会析构,而 node1 _prev 管理, _prev属于node2成员,所以这就叫循环引用,谁也不会释放。

// 解决方案:在引用计数的场景下,把节点中的_prev和_next改成weak_ptr就可以了
// 原理就是,node1->_next = node2;和node2->_prev = node1;时weak_ptr的_next和_prev不会增加
node1和node2的引用计数。
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; }

 

 如果不是new出来的对象如何通过智能指针管理呢?其实shared_ptr设计了一个删除器来解决这个问题,C++设计定制删除器来删除。

unique_ptr的删除器

class A
{
public:
};
struct DeleteArray
{
	void  operator()(A* ptr)
	{
		delete[] ptr;
	}
};

int main()
{
	unique_ptr<A> sp(new A);
	unique_ptr<A, DeleteArray> sp2(new A[10]);
}

unique_ptr的定制删除器是在模板参数位置传入。而shared_ptr的定制删除器是在构造函数位置出传入。

class A
{
public:
};
template<class T>
struct DeleteArray
{
	void  operator()(T* ptr)
	{
		delete[] ptr;
	}
};

int main()
{
	//unique_ptr<A> sp(new A);
	//unique_ptr<A, DeleteArray> sp2(new A[10]);
	shared_ptr<A> sp(new A);
	shared_ptr<A> sp1(new A[10], DeleteArray<A>());
}

4.C++11boost中智能指针的关系

1. C++ 98 中产生了第一个智能指针 auto_ptr.
2. C++ boost 给出了更实用的 scoped_ptr shared_ptr weak_ptr.
3. C++ TR1 ,引入了 shared_ptr 等。不过注意的是 TR1 并不是标准版。
4. C++ 11 ,引入了 unique_ptr shared_ptr weak_ptr 。需要注意的是 unique_ptr 对应 boost
scoped_ptr 。并且这些智能指针的实现原理是参考 boost 中的实现的

5.RAII扩展学习

#include <thread>
#include <mutex>
// C++11的库中也有一个lock_guard,下面的LockGuard造轮子其实就是为了学习他的原理
template<class Mutex>
class LockGuard
{
public:
 LockGuard(Mutex& mtx)
 :_mutex(mtx)
 {
 _mutex.lock();
 }
 ~LockGuard()
 {
 _mutex.unlock();
 }
 LockGuard(const LockGuard<Mutex>&) = delete;
private:
 // 注意这里必须使用引用,否则锁的就不是一个互斥量对象
 Mutex& _mutex;
};
mutex mtx;
static int n = 0;
void Func()
{
 for (size_t i = 0; i < 1000000; ++i)
 {
 LockGuard<mutex> lock(mtx);
 ++n;
 }
}
int main()
{
 int begin = clock();
 thread t1(Func);
 thread t2(Func);
 t1.join();
 t2.join();
 int end = clock();
 cout << n << endl;
 cout <<"cost time:" <<end - begin << endl;
 
 return 0; }

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值