C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)

文章目录

  • 4.智能指针[shared_ptr]
    • 4.1设计理念
      • 成员属性
    • 4.2主要接口
      • 拷贝构造
    • 4.3引用计数线程安全问题
      • 测试线程安全
        • 通过对计数引用的加锁保护使得类线程安全
        • 类实例化的对象使用时需要手动加锁保护
      • "锁"的引进
      • 线程引用传参问题
    • 4.4整体代码
  • 5.循环引用问题
    • 5.1问题的引入
    • 5.2分析造成此问题的原因
    • 5.3weak_ptr的主要代码
  • 6.数组对象的删除问题
    • 6.1代码问题
    • 6.2std::shared_ptr面对此问题的解决方案
      • 1.首先看std::shared_ptr::~shared_ptr
      • 2.删除器的传参及使用
      • 3.添加封装删除器
  • 7.总结
    • 7.1完整代码
    • 7.2C++11和Boost智能指针的关系

4.智能指针[shared_ptr]

4.1设计理念

成员属性

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第1张图片
C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第2张图片

每一个对象除了有一个主指针外 还有一个副指针用来计数 为什么不设置成int而设置成int*?

同一块空间被两个指针指向 当编译器得到需要销毁指针的指令时 会先判断这是不是最后一个指针 即count==1 时才释放空间 其余情况均只是的计数-- 而不释放空间 这是我们的最初目的 如果设置成int意味着每一个对象有各自的count 当ptr1拷贝给ptr2 我们想要的是 有一块空间单独来计数 如果执行拷贝则计数++ 即我们需要一块共有的空间来实现

不是要实现共有吗 为什么不直接用静态变量?

静态变量可以实现共有没错 但他是当前类的所有对象共有 举例说明: 代码的本意是ptr1 ptr2 ptr3指向同一块空间 此时count == 3 ptr4指向另一块空间时 代码的本意是count = 1 但是同时改变了ptr1/2/3

4.2主要接口

拷贝构造

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第3张图片

	//赋值重载
	//1."自己"给"自己"赋值
	//不仅仅是p1 = p1 还要考虑p2 = p1 但之前p2就 = p1
	//2.左 = 右 赋值后 
	// 左指针指向空间的指针少了一个 左指针的count-- 进一步考虑count--后==0的情况
	// 右指针指向空间的指针多了一个 右指针的count++
	shared_ptr<T>& operator=(const shared_ptr<T>& sp)
	{
		//"自己"给"自己"赋值: "自己"的本质
		if (_ptr != sp._ptr)
		{
			//count--
			Release();

			_ptr = sp._ptr;
			_pcount = sp._pcount;
			_pmtx = sp._pmtx;

			//count++
			AddCount();
		}

		return *this;
	}

4.3引用计数线程安全问题

测试线程安全

通过对计数引用的加锁保护使得类线程安全
	struct Date
	{
		int _year = 0;
		int _month = 0;
		int _day = 0;
	};
	void SharePtrFunc(ape::shared_ptr<Date>& sp, size_t n, mutex& mtx)
	{
		cout << "   SharePtrFunc: sp.GetPtr() == " << sp.GetPtr() << endl;

		for (size_t i = 0; i < n; ++i)
		{
			ape::shared_ptr<Date> copy(sp);
		}
	}

	void test_shared_safe()
	{
		ape::shared_ptr<Date> p(new Date);
		cout << "test_shared_safe: p.GetPtr() == " << p.GetPtr() << endl;

		const size_t n = 10000;
		mutex mtx;

		//线程引用传参即便mtx已经是引用 此处仍然需要使用库函数ref();
		thread t1(SharePtrFunc, ref(p), n, ref(mtx));
		thread t2(SharePtrFunc, ref(p), n, ref(mtx));

		t1.join();
		t2.join();

		cout << "p.GetCount(): == " << p.GetCount() << endl;
	}

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第4张图片

类实例化的对象使用时需要手动加锁保护

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第5张图片
C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第6张图片

"锁"的引进

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第7张图片

线程引用传参问题

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第8张图片

简单理解为 p在传给sp前是需要先调用线程的构造函数的 期间发生了某种动作 使得失去引用属性

4.4整体代码

template<class T>
class shared_ptr
{
public:
	//构造函数
	shared_ptr(T* ptr)
		:_ptr(ptr)
		, _pcount(new int(1))
		, _pmtx(new mutex)
	{

	}

	void Release()
	{
		//上锁
		_pmtx->lock();
		//不可释放锁
		bool deleteFlag = false;

		if (--(*_pcount) == 0)
		{
			cout << "delete:" << _ptr << endl;
			delete _ptr;
			delete _pcount;
			//可释放锁
			deleteFlag = true;
		}
		//解锁
		_pmtx->unlock();
		//判断并释放锁
		if (deleteFlag)
		{
			delete _pmtx;
		}
	}
	//void Release()
	//{
	//	_pmtx->lock();
	//	bool deleteFlag = false;
	//	if (--(*_pcount) == 0)
	//	{
	//		if (_ptr)
	//		{
	//			//cout << "delete:" << _ptr << endl;
	//			//delete _ptr;
	//
	//			// 删除器进行删除
	//			_del(_ptr);
	//		}
	//
	//		delete _pcount;
	//		deleteFlag = true;
	//	}
	//
	//	_pmtx->unlock();
	//
	//	if (deleteFlag)
	//	{
	//		delete _pmtx;
	//	}
	//}
	void AddCount()
	{
		_pmtx->lock();

		++(*_pcount);

		_pmtx->unlock();
	}

	//拷贝构造
	shared_ptr(const shared_ptr<T>& sp)
		:_ptr(sp._ptr)
		, _pcount(sp._pcount)
		, _pmtx(sp._pmtx)
	{
		AddCount();
	}

	//赋值重载
	//1."自己"给"自己"赋值
	//不仅仅是p1 = p1 还要考虑p2 = p1 但之前p2就 = p1
	//2.左 = 右 赋值后 
	// 左指针指向空间的指针少了一个 左指针的count-- 进一步考虑count--后==0的情况
	// 右指针指向空间的指针多了一个 右指针的count++
	shared_ptr<T>& operator=(const shared_ptr<T>& sp)
	{
		//"自己"给"自己"赋值: "自己"的本质
		if (_ptr != sp._ptr)
		{
			//count--
			Release();

			_ptr = sp._ptr;
			_pcount = sp._pcount;
			_pmtx = sp._pmtx;

			//count++
			AddCount();
		}

		return *this;
	}

	//析构函数
	~shared_ptr()
	{
		Release();
	}

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

	T* operator->()
	{
		return _ptr;
	}

	T* GetPtr()
	{
		return _ptr;
	}

	int GetCount()
	{
		return *_pcount;
	}

private:
	T* _ptr;
	int* _pcount;
	mutex* _pmtx;
};

void test_shared()
{
	shared_ptr<int> sp1(new int(1));
	shared_ptr<int> sp2(sp1);
	shared_ptr<int> sp3(sp2);

	shared_ptr<int> sp4(new int(10));

	sp1 = sp4;
	sp4 = sp1;

	sp1 = sp1;
	sp1 = sp2;
}


//   线程安全问题   ///
struct Date
{
	int _year = 0;
	int _month = 0;
	int _day = 0;
};

void SharePtrFunc(ape::shared_ptr<Date>& sp, size_t n, mutex& mtx)
{
	cout << "   SharePtrFunc: sp.GetPtr() == " << sp.GetPtr() << endl;

	cout << "   SharePtrFunc: &sp == " << &sp << endl;

	for (size_t i = 0; i < n; ++i)
	{
		ape::shared_ptr<Date> copy(sp);

		mtx.lock();

		sp->_year++;
		sp->_day++;
		sp->_month++;

	    mtx.unlock();
	}
}

void test_shared_safe()
{
	ape::shared_ptr<Date> p(new Date);
	cout << "test_shared_safe: p.GetPtr() == " << p.GetPtr() << endl;

	cout << "test_shared_safe: &p == " << &p << endl;

	const size_t n = 100000;
	mutex mtx;

	//线程引用传参即便p和mtx已经是引用 此处仍然需要使用库函数ref();
	thread t1(SharePtrFunc, ref(p), n, ref(mtx));
	thread t2(SharePtrFunc, ref(p), n, ref(mtx));

	t1.join();
	t2.join();

	cout << "p.GetCount(): == " << p.GetCount() << endl;

	cout << "p->_year  == " << p->_year << endl;
	cout << "p->_month == " << p->_month << endl;
	cout << "p->_month == " << p->_month << endl;

}

5.循环引用问题

5.1问题的引入

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第9张图片
C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第10张图片
C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第11张图片
C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第12张图片
C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第13张图片
C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第14张图片

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第15张图片

5.2分析造成此问题的原因

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第16张图片

为了解决此问题 需要引进weaked_ptr 我们需要了解的是
智能指针shared_ptr满足

  1. 符合RAII思想
  2. 可以像指针一样使用
  3. 支持拷贝

智能指针weaked_ptr满足

  1. 不符合RAII思想
  2. 可以像指针一样使用
  3. 辅助解决shared_ptr的循环引用问题
  4. weaked_ptr可以指向资源,但是不参与管理,不增加引用计数

实际上库里的智能指针远比我们上述讲到的复杂得多 为了便于学习和理解 我们只学习核心框架 需要了解的是 库里所支持的这两个函数C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第17张图片
weaked_ptr有自己的count 配合expired来判断所指向资源是否还有被指向的必要 即weaked_ptr可以指向资源,但是不参与管理,不增加shared_ptr引用计数 所以存在于一种所指向资源的计数已经成0 此时expired判断是否失效 若所指向资源失效weaked_ptr就不再指向

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第18张图片

5.3weak_ptr的主要代码

template<class T>
class weak_ptr
{
public:
	weak_ptr()
		:_ptr(nullptr)
	{
	
	}

	weak_ptr(const shared_ptr<T>& sp)
		:_ptr(sp.GetPtr())
	{
	
	}

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

	T* operator->()
	{
		return _ptr;
	}

	T* GetPtr()
	{
		return _ptr;
	}

private:
	T* _ptr;
};

6.数组对象的删除问题

6.1代码问题

ape::shared_ptr sparr(new Date[10]);
当使用我们自己模拟实现的简洁版shared_ptr时 上述代码会报错(手动实现Date的析构函数时会报错 不手动实现调用库的析构函数不报错 因为当手动实现了析构函数 Date的空间前会有一个4字节的空间用来存放实例化对象的个数 以便知道调用几次析构函数 但是此时析构函数应该先向前偏移4字节 以便析构时把这4个字节也释放

6.2std::shared_ptr面对此问题的解决方案

1.首先看std::shared_ptr::~shared_ptr

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第19张图片

即库里的shared_ptr::~shared_ptr与我们写的析构函数不同之处在于 一个对象在实例化时 他会判断是否接受了参数deleter 如果接收则析构时调用deleter析构 若没有接收deleter则正常析构

2.删除器的传参及使用

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第20张图片

销毁对象。但是,以前,根据成员use_count的值,它可能会产生以下副作用:如果use_count大于1(即该对象与其他shared_ptr对象共享其托管对象的所有权):与其共享所有权的其他对象的使用计数将减少1。如果use_coount为1(即,该对象是托管指针的唯一所有者):它所拥有的指针被删除(如果shared_ptr对象是用特殊的deleter构造的,则调用它;否则,函数使用运算符delete)。如果use_count为零(即对象为空),则此析构函数没有副作用。

	template<class T>
	struct DeleteArray
	{
		void operator()(T* ptr)
		{
			cout << "匿名对象DeleteArray(): " << ptr << endl;
			delete[] ptr;
		}
	};
	void test_std_shared_deletor()
	{
		//template  
		//shared_ptr (U* p, D del);   带删除器的构造函数

		std::shared_ptr<Date> sparr1(new Date[10], DeleteArray<Date>());

		std::shared_ptr<Date> sparr2(new Date[10],
			[](Date* ptr)
			{
				cout << "lambda表达式 delete[]: " << ptr << endl;
				delete[] ptr;
			}
		);

		auto deleter = [](Date* ptr)
			{
				cout << "lambda表达式 delete[]: " << ptr << endl;
				delete[] ptr;
			};
		std::shared_ptr<Date> sparr3(new Date[10], deleter);

		std::shared_ptr<FILE> spFile(fopen("Test.cpp", "r"),
			[](FILE* ptr)
			{
				cout << "lambda表达式 delete[]: " << ptr << endl;
				fclose(ptr);
			}
		);
	}

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第21张图片

3.添加封装删除器

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第22张图片

C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)_第23张图片

7.总结

7.1完整代码

#pragma once

#include 
#include 
#include 

namespace ape
{
	template<class T>
	class shared_ptr
	{
	public:
		//构造函数
		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			, _pcount(new int(1))
			, _pmtx(new mutex)
		{

		}

		//删除器构造函数
		template<class D>
		shared_ptr(T* ptr, D del)
			:_ptr(ptr)
			, _pcount(new int(1))
			, _pmtx(new mutex)
			, _del(del)
		{
		
		}

		//非删除器Release()
		/*
		void Release()
		{
			//上锁
			_pmtx->lock();
			//不可释放锁
			bool deleteFlag = false;

			if (--(*_pcount) == 0)
			{
				if (_ptr != nullptr)
				{
					cout << "delete:" << _ptr << endl;
					delete _ptr;
				}
				
				delete _pcount;
				//可释放锁
				deleteFlag = true;
			}
			//解锁
			_pmtx->unlock();
			//判断并释放锁
			if (deleteFlag)
			{
				delete _pmtx;
			}
		}
		*/
		
		//删除器Release()
		void Release()
		{
			_pmtx->lock();
			bool deleteFlag = false;

			if (--(*_pcount) == 0)
			{
				if (_ptr != nullptr)
				{
					_del(_ptr);
				}
		
				delete _pcount;
				deleteFlag = true;
			}
		
			_pmtx->unlock();
		
			if (deleteFlag)
			{
				delete _pmtx;
			}
		}
		void AddCount()
		{
			_pmtx->lock();

			++(*_pcount);

			_pmtx->unlock();
		}

		//拷贝构造
		shared_ptr(const shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			, _pcount(sp._pcount)
			, _pmtx(sp._pmtx)
		{
			AddCount();
		}

		//赋值重载
		//1."自己"给"自己"赋值
		//不仅仅是p1 = p1 还要考虑p2 = p1 但之前p2就 = p1
		//2.左 = 右 赋值后 
		// 左指针指向空间的指针少了一个 左指针的count-- 进一步考虑count--后==0的情况
		// 右指针指向空间的指针多了一个 右指针的count++
		shared_ptr<T>& operator=(const shared_ptr<T>& sp)
		{
			//"自己"给"自己"赋值: "自己"的本质
			if (_ptr != sp._ptr)
			{
				//count--
				Release();

				_ptr = sp._ptr;
				_pcount = sp._pcount;
				_pmtx = sp._pmtx;

				//count++
				AddCount();
			}

			return *this;
		}

		//析构函数
		~shared_ptr()
		{
			Release();
		}

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

		T* operator->()
		{
			return _ptr;
		}

		T* GetPtr() const
		{
			return _ptr;
		}

		int GetCount() const
		{
			return *_pcount;
		}

	private:
		T* _ptr;
		int* _pcount;
		mutex* _pmtx;

		//包装器
		//缺省值处理情况: ape::shared_ptr sp(new Date);
		//因为你析构时默认使用删除器 那么遇到没有显示传的要使用缺省值

		//构造函数传deleter时 可以是仿函数 lambda表达式 函数指针 此处用包装器接收
		function<void(T*)> _del = [](T* ptr)
			{
				cout << "lambda表达式 delete: " << ptr << endl;
				delete ptr;
			};
	};

	void test_shared()
	{
		shared_ptr<int> sp1(new int(1));
		shared_ptr<int> sp2(sp1);
		shared_ptr<int> sp3(sp2);

		shared_ptr<int> sp4(new int(10));

		sp1 = sp4;
		sp4 = sp1;

		sp1 = sp1;
		sp1 = sp2;
	}


	//   线程安全问题   ///
	struct Date
	{
		int _year = 0;
		int _month = 0;
		int _day = 0;

		~Date()
		{

		}
	};

	void SharePtrFunc(ape::shared_ptr<Date>& sp, size_t n, mutex& mtx)
	{
		cout << "   SharePtrFunc: sp.GetPtr() == " << sp.GetPtr() << endl;

		cout << "   SharePtrFunc: &sp == " << &sp << endl;

		for (size_t i = 0; i < n; ++i)
		{
			ape::shared_ptr<Date> copy(sp);

			mtx.lock();

			sp->_year++;
			sp->_day++;
			sp->_month++;

		    mtx.unlock();
		}
	}

	void test_shared_safe()
	{
		ape::shared_ptr<Date> p(new Date);
		cout << "test_shared_safe: p.GetPtr() == " << p.GetPtr() << endl;

		cout << "test_shared_safe: &p == " << &p << endl;

		const size_t n = 100000;
		mutex mtx;

		//线程引用传参即便p和mtx已经是引用 此处仍然需要使用库函数ref();
		thread t1(SharePtrFunc, ref(p), n, ref(mtx));
		thread t2(SharePtrFunc, ref(p), n, ref(mtx));

		t1.join();
		t2.join();

		cout << "p.GetCount(): == " << p.GetCount() << endl;

		cout << "p->_year  == " << p->_year << endl;
		cout << "p->_month == " << p->_month << endl;
		cout << "p->_month == " << p->_month << endl;

	}

	template<class T>
	class weak_ptr
	{
	public:
		weak_ptr()
			:_ptr(nullptr)
		{
		
		}

		weak_ptr(const shared_ptr<T>& sp)
			:_ptr(sp.GetPtr())
		{
		
		}

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

		T* operator->()
		{
			return _ptr;
		}

		T* GetPtr()
		{
			return _ptr;
		}

	private:
		T* _ptr;
	};

	// 循环引用
	struct ListNode
	{

		/*普通写法
		ListNode* _next;
		ListNode* _prev;
		int _val;
		*/

		
		/*shared_ptr
		ape::shared_ptr _next;
		ape::shared_ptr _prev;
		int _val;
		*/

		//weaked_ptr
		ape::weak_ptr<ListNode> _next;
		ape::weak_ptr<ListNode> _prev;
		int _val;

		~ListNode()
		{
			cout << "~ListNode()" << endl;
		}
	};

	// 循环引用
	void test_shared_cycle()
	{
		/*
		常规写法 -- 抛异常场景不适合
		ListNode* n1 = new ListNode;
		ListNode* n2 = new ListNode;

		n1->_next = n2;
		n2->_prev = n1;

		delete n1;
		delete n2;
		*/

		//智能指针写法
		ape::shared_ptr<ListNode> n1(new ListNode);
		ape::shared_ptr<ListNode> n2(new ListNode);

		//内置类型 = 自定义类型 -- error
		/*
		ListNode* _next;
		ListNode* _prev;
		int _val;

		ape::shared_ptr n1(new ListNode);
		ape::shared_ptr n2(new ListNode);

		n1->_next = n2;
		n2->_prev = n1;
		*/

		/*shared_ptr: 引发循环引用问题
		ape::shared_ptr _next;
		ape::shared_ptr _prev;
		int _val;

		ape::shared_ptr n1(new ListNode);
		ape::shared_ptr n2(new ListNode);

		n1->_next = n2;
		n2->_prev = n1;
		*/
		
		/*weak_ptr: 解决循环引用
		ape::weak_ptr _next;
		ape::weak_ptr _prev;
		int _val;

		ape::shared_ptr n1(new ListNode);
		ape::shared_ptr n2(new ListNode);
        */
		cout << "n1.GetCount() == " << n1.GetCount() << endl;
		cout << "n2.GetCount() == " << n2.GetCount() << endl;
		n1->_next = n2;
		n2->_prev = n1;
		cout << "n1.GetCount() == " << n1.GetCount() << endl;
		cout << "n2.GetCount() == " << n2.GetCount() << endl;

	}

/
	 //定制删除器 -- 可调用对象
	
	template<class T>
	struct DeleteArray
	{
		void operator()(T* ptr)
		{
			cout << "匿名对象DeleteArray(): " << ptr << endl;
			delete[] ptr;
		}
	};

	//std库deleter的学习
	/*
	void test_std_shared_deletor()
	{
		//template  
		//shared_ptr (U* p, D del);   带删除器的构造函数

		std::shared_ptr sparr1(new Date[10], DeleteArray());

		std::shared_ptr sparr2(new Date[10],
			[](Date* ptr)
			{
				cout << "lambda表达式 delete[]: " << ptr << endl;
				delete[] ptr;
			}
		);

		auto deleter = [](Date* ptr)
			{
				cout << "lambda表达式 delete[]: " << ptr << endl;
				delete[] ptr;
			};
		std::shared_ptr sparr3(new Date[10], deleter);

		std::shared_ptr spFile(fopen("Test.cpp", "r"),
			[](FILE* ptr)
			{
				cout << "lambda表达式 delete[]: " << ptr << endl;
				fclose(ptr);
			}
		);
	}
	*/

	//删除器构造函数
	/*
	template
	shared_ptr(T* ptr, D del)
		:_ptr(ptr)
		, _pcount(new int(1))
		, _pmtx(new mutex)
		, _del(del)
	{

	}
	*/
	void test_ape_shared_deleter()
	{ 
		ape::shared_ptr<Date> sp(new Date);

		ape::shared_ptr<Date> sparr1(new Date[10], DeleteArray<Date>());
		ape::shared_ptr<Date> sparr2(new Date[10], 
			[](Date* ptr) 
			{
			cout << "lambda表达式 delete[]: " << ptr << endl;
			delete[] ptr;
			}
		);

		auto deleter = [](Date* ptr)
			{
				cout << "lambda表达式 delete[]: " << ptr << endl;
				delete[] ptr;
			};
		ape::shared_ptr<Date> sparr3(new Date[10], deleter);

		ape::shared_ptr<FILE> spFile(fopen("Test.cpp", "r"),
			[](FILE* ptr)
			{
			cout << "lambda表达式 delete[]: " << ptr << endl;
			fclose(ptr);
			}
		);
	}
}


7.2C++11和Boost智能指针的关系

  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实现]

你可能感兴趣的:(遣返回家的C家家,c++,java,开发语言)