当前位置:   article > 正文

模板共享指针(shared_ptr)原理实现_共享指针的实现

共享指针的实现

  最近在书中看到关于智能指针的描述,由于之前没有使用过智能指针,便通过调试源代码(源代码的实现有点杂乱,并不能以最简单直观的方式呈现)了解原理后,以简单直接的方式写了一个shared_ptr指针类。

关于shared_ptr指针的几点介绍:
   1.共享指针在拷贝函数中拷贝已有的指针对象参数地址达到共享数据(简单的说就是一块类对象地址由多个指针同时指向并且使用);
   2.共享指针内部通过计数形式来记录共享调用类对象内存的次数(创建共享指针对象时计数为1,每被拷贝一次(赋值给别的新对象)计数加1),每释放一个共享指针对象计数减1,当指针计数为0时释放共享调用类对象内存;
   3.shared_ptr类共享指针内存及共享计数内存在没有通过手动释放的情况下,将会通过虚拟析构函数来做内存释放工作。

下面贴上代码:

template<typename _Tp>
	class new_allocator//采用了模板类特有的内存分配类,保证分配产生碎片变少的同时并且提高运行效率
	{
	public:
		typedef size_t     size_type;
		typedef ptrdiff_t  difference_type;
		typedef _Tp*       pointer;
		typedef const _Tp* const_pointer;
		typedef _Tp&       reference;
		typedef const _Tp& const_reference;
		typedef _Tp        value_type;

		template<typename _Tp1>
		struct rebind
		{ typedef new_allocator<_Tp1> other; };

		new_allocator() throw() { }

		new_allocator(const new_allocator&) throw() { }

		template<typename _Tp1>
		new_allocator(const new_allocator<_Tp1>&) throw() { }

		~new_allocator() throw() { }

		pointer
			address(reference __x) const { return &__x; }

		const_pointer
			address(const_reference __x) const { return &__x; }

		// NB: __n is permitted to be 0.  The C++ standard says nothing
		// about what the return value is when __n == 0.
		pointer
			allocate(size_type __n, const void* = 0)
		{ 
			/*if (__builtin_expect(__n > this->max_size(), false))
			__throw_bad_alloc();*/

			return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
		}

		// __p is not permitted to be a null pointer.
		void
			deallocate(pointer __p, size_type)
		{ ::operator delete(__p); }

		size_type
			max_size() const throw() 
		{ return size_t(-1) / sizeof(_Tp); }

		// _GLIBCXX_RESOLVE_LIB_DEFECTS
		// 402. wrong new expression in [some_] allocator::construct
		void 
			construct(pointer __p, const _Tp& __val) 
		{ ::new((void *)__p) _Tp(__val); }

#ifdef __GXX_EXPERIMENTAL_CXX0X__
		template<typename... _Args>
		void
			construct(pointer __p, _Args&&... __args)
		{ ::new((void *)__p) _Tp(forward<_Args>(__args)...); }
#endif

		void 
			destroy(pointer __p) { __p->~_Tp(); }
	};




	template<typename _Tp>
	class allocator: public new_allocator<_Tp>
	{
	public:
		typedef size_t     size_type;
		typedef ptrdiff_t  difference_type;
		typedef _Tp*       pointer;
		typedef const _Tp* const_pointer;
		typedef _Tp&       reference;
		typedef const _Tp& const_reference;
		typedef _Tp        value_type;

		template<typename _Tp1>
		struct rebind
		{ typedef allocator<_Tp1> other; };

		allocator() throw() { }

		allocator(const allocator& __a) throw()
			: __glibcxx_base_allocator<_Tp>(__a) { }

		template<typename _Tp1>
		allocator(const allocator<_Tp1>&) throw() { }

		~allocator() throw() { }

		// Inherit everything else.
	};




	class Ref_count_base/* 指针计数管理 */
	{
	public:
		Ref_count_base():m_ncount(0)
		{

		}
		long GetCount()//获取当前计数
		{
			return m_ncount;
		}
		int ResetCount(int nAdd)//设置当前计数 1. 计数增加1 ,0. 计数减少1
		{
			if (1 == nAdd)
			{
				++m_ncount;
			}
			else
			{
				--m_ncount;
			}
			return m_ncount;
		}
	private:
		long m_ncount;//计数变量
	};


	template <typename _Ty>
	class Point_Base/* 在这里做指针计数与数据对象的结合并且都是采用动态分配的方式(通过使用地址)达到数据共享  */
	{
	public:
		Point_Base()
		{
			ref_count = 0;
		}
		typedef _Ty* pointer;
		typedef _Ty& reference;
	protected:
		pointer m_obj;//共享类指针对象
		Ref_count_base* ref_count;//计数类指针对象
	};




template <typename Pointer>
class Test_Pointer : public Point_Base<Pointer>//模板指针调用类
{
	typedef Test_Pointer<Pointer> test_pointer;
	typedef Pointer* pointer;
	typedef Pointer& reference;
	typedef allocator<Pointer> alloc_type;
public:
	
	virtual ~Test_Pointer()//每次类退出前会执行一次析构函数
	{
		if (!ref_count)/* 已经释放的情况下直接返回 */
		{
			return ;
		}
		int ncount = ref_count->ResetCount(0);
		if (0 == ncount && 0 != m_obj)//如果计数为0而且共享类指针还没有释放,那么这个共享指针没被共享和计数指针一起释放掉
		{
			m_alloc.deallocate(m_obj,sizeof(Pointer) * m_nSize); 
			delete ref_count;
		}
	}
	Test_Pointer():m_nCritical(0),m_nCriticalRun(1),m_nSize(1)//无参数构造函数 分配一个共享类内存
	{
		if (0 == ref_count)
		{
			ref_count = new Ref_count_base;
		}
		ref_count->ResetCount(1);
		m_obj = m_alloc.allocate(1);
	}

	template<class _Ux>
	explicit Test_Pointer(_Ux *_Px):m_nCritical(0),m_nCriticalRun(1)//有参数构造函数 根据输入的数量进行分配内存
	{
		if (0 == ref_count)
		{
			ref_count = new Ref_count_base;
		}
		ref_count->ResetCount(1);
		m_obj = m_alloc.allocate(*_Px);
		m_nSize = *_Px;
	}
	pointer operator->() /* 增加临时临界区,在多个线程中保证智能指针数据同步,关于调用类对象的数据需要在线程中增加真正的临界区来同步化 */
	{
		if (1 == m_nCriticalRun)
		{
			int nRun = m_nCritical;
			++m_nCritical;
			while (nRun != 0 && nRun != m_nCritical)
				;
			--m_nCritical;
		}
		return m_obj;
	}

	Test_Pointer(test_pointer& _Other)//复制构造函数
	{
		*this = _Other;
		ref_count->ResetCount(1);
	}
	void SetCritical(int nRun);//调用共享类成员数据保护临界区 1. 启动 0. 不启动 默认1(在多线程情况下可以看出效果)
	void Reset();
	template<class _Ux>
	void Reset(_Ux *_Px)//把共享计数减一后 ,分配新的共享类内存
	{
		if (ref_count && 0 < ref_count->GetCount() && 0 != m_obj)
		{
			ref_count->ResetCount(0);

			if (0 == ref_count->GetCount())/* 如果在共享指针计数减一为0的情况(属于没有在别的地方被使用) ,释放共享指针 和共享指针计数 */
			{
				m_alloc.deallocate(m_obj,sizeof(Pointer) * m_nSize); 
				delete ref_count;
			}
		}

		
			ref_count = new Ref_count_base;
		
		ref_count->ResetCount(1);
		m_obj = m_alloc.allocate(*_Px);
		m_nSize = *_Px;
	}
private:	
	alloc_type m_alloc;//内存分配
	volatile int m_nCritical;//临界区
	volatile int m_nCriticalRun;//临界区是否运行
	int m_nSize;//共享类对象内存大小(个数)
};
template <typename Pointer>
inline void Test_Pointer<Pointer>::SetCritical(int nRun)
{
	m_nCriticalRun = nRun;
}
template <typename Pointer>
inline void Test_Pointer<Pointer>::Reset()/* 调用无参数Reset 把共享指针计数减一 并且把共享指针计数与共享指针赋值为0 */
{
	if (ref_count && 0 < ref_count->GetCount() && 0 != m_obj)
	{
		ref_count->ResetCount(0);
		
		if (0 == ref_count->GetCount())/* 如果在共享指针计数减一为0的情况(属于没有在别的地方被使用) ,释放共享指针 和共享指针计数 */
		{
			m_alloc.deallocate(m_obj,sizeof(Pointer) * m_nSize); 
			delete ref_count;
		}
		m_obj = 0;
		ref_count = 0;/* 因为已经脱离了上一个共享指针,所以共享指针 和共享指针计数都指向空 */
		m_nSize = 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
  • 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
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261

使用方法:

Test_Pointer<int> testint(new int(2));
	Test_Pointer<int> testint1 = testint;
	Test_Pointer<int> testint2 = testint1;
	Test_Pointer<int> testint3 = testint;//指向到此处可以看到4个类对象共享类指针地址与共享计数相同
	testint.Reset();//共享计数减一 并且共享类指针地址与共享计数地址赋值为0
	testint1.Reset(new int(4));//共享计数减一 并且共享类指针地址与共享计数地址赋值为0 , 然后分配新的共享类指针内存 与 共享计数
	testint1.SetCritical(0);	
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/206299?site
推荐阅读
相关标签
  

闽ICP备14008679号