C++:模拟实现带weak_ptr的shared_ptr

本文详细介绍了C++智能指针shared_ptr和weak_ptr的内部结构及实现,包括构造函数、析构函数、拷贝与移动操作、引用计数管理等关键功能,并提供了完整的仿写代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

上篇文章对shared_ptr进行了简单的仿写,在这篇文章中,将引入它的小弟weak_ptr,两者之间的关系将由本文来解析。

普通的shared_ptr仿写:【点击这里查看】

一、对带有weak_ptr的shared_ptr进行仿写

1. 成员组成内存结构简图

这种shared_ptr的成员变量有三个:

  1. 直接指向对象的指针_Ptr;
  2. 指向引用计数类型的指针_Rep;
  3. 删除器类型。

接下来实现引用计数类和删除器类。

在这里插入图片描述

(一)删除器类型

1.删除单个对象

template<class _Ty>
class MyDeletor
{
public:
	MyDeletor() = default;
	void operator()(_Ty* ptr) const
	{
		if (ptr != nullptr)
		{
			delete ptr;// one 
		}
	}
};

2.删除一组对象

template<class _Ty>
class MyDeletor<_Ty[]>
{
public:
	MyDeletor() = default;
	void operator()(_Ty* ptr) const
	{
		if (ptr != nullptr)
		{
			delete[]ptr; // array
		}
	}
};

(二)引用计数类型

有三个成员变量:

  1. 指向对象的指针_Ptr;
  2. shared_ptr的引用计数;
  3. weak_ptr的引用计数。

构造函数

  1. 让值类型指针指向 参数所指向的对象;
  2. shared_ptr和weak_ptr的引用计数都置为1。

_Incref():对shared_ptr的引用计数扩容(+1);
_Incwref():对weak_ptr的引用计数扩容(+1);

#include<atomic>

template<class _Ty>
class RefCnt
{
public:
	_Ty* _Ptr;               // Obj;
	std::atomic_int _Uses;   // shared_ptr;
	std::atomic_int _Weaks;  // weak_ptr;

public:
	RefCnt(_Ty* p) :_Ptr(p), _Uses(1), _Weaks(1) {}
	~RefCnt() {}
	void _Incref() { _Uses += 1; }
	void _Incwref() { _Weaks += 1; }
};

my_shared_ptr类的设计

1. 构造函数

template<class _Ty, class _Dx = MyDeletor<_Ty> >
class my_shared_ptr
{
private:
	_Ty*           _Ptr; // Object;
	RefCnt<_Ty>  * _Rep;
	_Dx _mDeletor;
public:
	my_shared_ptr(_Ty* p = nullptr) :_Ptr(nullptr), _Rep(nullptr)
	{
		if (p != nullptr)
		{
			_Ptr = p;
			_Rep = new RefCnt<_Ty>(p);
		}
	}
};

对于构造函数:
初始化列表先将两个指针置为nullptr,如果参数p不为nullptr,那么让_Ptr也指向p所指的对象;然后向堆区申请一个该值类型的引用计数对象,让_Rep指向该对象。

如图所示:
在这里插入图片描述

2. 析构函数

来通过判断_Rep来析构。

~my_shared_ptr()
{
	if (_Rep != nullptr && --_Rep->_Uses == 0)
	{
		_mDeletor(_Ptr);
		if (--_Rep->_Weaks == 0)
		{
			delete _Rep;
		}
	}
	_Ptr = nullptr;
	_Rep = nullptr;
}

3. 拷贝构造函数

my_shared_ptr(const my_shared_ptr& _Y):_Ptr(_Y._Ptr),_Rep(_Y._Rep)
{
	if (_Rep != nullptr)
	{
		_Rep->_Incref(); // _Uses
	}
}

图解:
在这里插入图片描述

4. 移动构造函数

资源的转移:比较简单,只需要自身指针指向参数的指针所指之物;
再将参数的指针置为nullptr。

my_shared_ptr(my_shared_ptr&& other):_Ptr(other._Ptr),_Rep(other._Rep)
{
	other._Ptr = nullptr;
	other._Rep = nullptr;
}

5.赋值构造函数

my_shared_ptr& operator=(const my_shared_ptr& r)
{
	if (this == &r || this->_Ptr == r._Ptr) return *this;
	if (_Ptr != nullptr && --_Rep->_Uses == 0)
	{
		_mDeletor(_Ptr);
		if (--_Rep->_Weaks == 0)
		{
			delete _Rep;
		}
	}
	_Ptr = r._Ptr;
	_Rep = r._Rep;
	if (_Ptr != nullptr)
	{			
		_Rep->_Incref(); // _Uses;
	}
	return *this;
}
  1. 防止自赋值:当this指针和参数相同时,或两者的_Ptr指向同一对象,则直接返回。
  2. 如果_Ptr指向不为nullptr,且对shared_ptr的引用计数减一后为0,则先删除指向的对象,此时引用计数类型并未删除;
  3. 再对weak_ptr的引用计数减一,若为0,说明引用计数类型也没有存在的必要,这时候对其删除。
  4. 这时候再让当前的两个指针指向参数的指针所指向的资源
  5. 如果当前_Ptr指向的对象不为nullptr,则对shared_ptr的引用计数加一。
  6. 返回当前对象(*this)。

示例1:
如果是这种比较复杂的情况,就需要先把sp1所指向的对象析构掉。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

6. 移动赋值函数

my_shared_ptr& operator=(my_shared_ptr&& other)
{
	if (this == &other) return *this;
	if (_Ptr != nullptr && other._Ptr != nullptr && _Ptr == other._Ptr)
	{
		this->_Rep->_Uses -= 1;
		other._Ptr = nullptr;
		other._Rep = nullptr;
		return *this;
	}
	if (_Ptr != nullptr && --_Rep->_Uses == 0)
	{
		_mDeletor(_Ptr);
		if (--_Rep->_Weaks == 0)
		{
			delete _Rep;
		}
	}
	_Ptr = other._Ptr;
	_Rep = other._Rep;
	other._Ptr = nullptr;
	other._Rep = nullptr;
	return *this;
}

该函数算是这里面较难写的函数,和赋值函数一样,需要考虑多种情况。

  1. 防止自赋值。
  2. 若两个_Ptr不为nullptr且指向同一个对象,则将shared_ptr的引用计数减一,将参数的两个指针置为nullptr后返回。
  3. 如果1,2不满足,则判断当前指针是否为空,并对shared_ptr引用计数减一,如果减完为0,说明没有指针指向对象,则删除_Ptr;再对weak_ptr的引用计数减一,若为0,则删除引用计数结点。
  4. 处理完上面三步,此时让当前对象的_Ptr和_Ref指向参数的_Ptr和_Ref,完成资源的转移,再将参数的这些指针置为nullptr,返回*this。

7.获得指向对象的指针(_Ptr)

_Ty* get() const { return _Ptr; }

8.解引用和指向符重载

_Ty& operator*() const { return *get(); }
_Ty* operator->() const { return get(); }

9.返回当前持有该资源的shared_ptr个数

size_t use_count() const
{
	if (_Rep == nullptr) return 0;
	return _Rep->_Uses;
}

10.交换资源函数

void swap(my_shared_ptr& r)
{
	std::swap(_Ptr, r._Ptr);
	std::swap(_Rep, r._Rep);
}

11.bool()运算符重载

operator bool() const { return _Ptr != nullptr; }

12.友元类(my_weak_ptr)

template<class _Ty>
friend class my_weak_ptr;

二、weak_ptr的仿写

内存简图:

在这里插入图片描述

类的设计

只需要一个_Rep指针,指向引用计数结构

template<class _Ty>
class my_weak_ptr
{
private:
	RefCnt<_Ty>* _Rep;
public:
	my_weak_ptr() :_Rep(nullptr) {}
};

1. 构造函数:利用shared_ptr来构造

让指针指向共享型指针的引用计数结构。

my_weak_ptr(const my_shared_ptr<_Ty>& other)
	:_Rep(other._Rep)
{
	if (_Rep != nullptr)
	{
		_Rep->_Incwref(); // Weaks;
	}
}

2. 拷贝构造

让两个指针指向统一个引用计数结构,再对引用计数的 _Weaks加一,意思是现在多了一个weak_ptr来指向该结构。

my_weak_ptr(const my_weak_ptr& other)
	:_Rep(other._Rep)
{
	if (_Rep != nullptr)
	{
		_Rep->_Incwref();
	}
}

3.移动构造

只需要完成资源转移,再将参数的指向置为nullptr.

my_weak_ptr(my_weak_ptr&& other)
	: _Rep(other._Rep)
{
	other._Rep = nullptr;
}

4. 赋值构造函数

my_weak_ptr& operator=(const my_weak_ptr& other)
{
	if (this == &other || this->_Rep == other._Rep) return *this;
	if (_Rep != nullptr && --_Rep->_Weaks == 0)
	{
		delete _Rep;
	}
	_Rep = other._Rep;	
	if (_Rep != nullptr)
	{
		_Rep->_Incwref();
	}
	return *this;
}
  1. 如果自赋值或指向相同,则直接返回。
  2. 若当前指针不为nullptr且对弱引用计数减一后为0,则删除当前指向的引用计数结构。若减一后不为0,则执行第3步。
  3. 让当前_Rep指针指向参数的_Rep所指向的空间。
  4. 在赋值后,如果指向不为nullptr,则让弱引用计数加一。
  5. 返回当前对象(*this)。

5. 移动构造函数

my_weak_ptr& operator=(my_weak_ptr&& other)
{
	if (this == &other) return *this;
	if (this->_Rep != nullptr && other._Rep != nullptr && _Rep == other._Rep)
	{
		this->_Rep->_Weaks -= 1;
		other._Rep = nullptr;
		return *this;
	}
	if (_Rep != nullptr && --_Rep->_Weaks == 0)
	{
		delete _Rep;
	}
	_Rep = other._Rep;
	other._Rep = nullptr;
	return *this;
}
  1. 防止自赋值
  2. 若两个指针都不为nullptr且指向同一个引用计数结构,那么只需要对弱引用计数减一(_Weaks–),再将参数的指针置为nullptr,返回当前对象即可。
  3. 在经过1、2步后,若当前指针不为nullptr,再对弱引用计数减一,等于0时,则删除引用计数结构,因为此时没有一个指针指向它。
  4. 这几步都过了之后,这时再将资源转移,返回当前对象*this。

6.赋值函数:用shared_ptr来赋值

my_weak_ptr& operator=(const my_shared_ptr<_Ty>& other)
{
	if (_Rep != nullptr && --_Rep->_Weaks == 0)
	{
		delete _Rep;
	}
	_Rep = other->_Rep;
	if (_Rep != nullptr)
	{
		_Rep->_Incwref();
	}
	return *this;
}
  1. 还是对弱引用计数减一判断,如果减完为0,则删除引用计数结构。
  2. 将_Rep指向将参数的_Rep所指向的结构。
  3. 如果此时_Rep不为nullptr,则将弱引用计数加一。
  4. 返回当前对象*this。

7. 析构函数

  1. 对_Rep判空,再对弱引用计数减一,等于0说明没有指针指向该引用计数结构,那么就删除该结构。
  2. 将_Rep置为nullptr。
~my_weak_ptr()
{
	if (_Rep != nullptr && --_Rep->_Weaks == 0)
	{
		delete _Rep;
	}
	_Rep = nullptr;	
}

8.检查被引用的对象是否已经删除

意思就是查看shared_ptr引用计数是否为0,如果为0说明该对象没有指针指向,已经被删除了。

bool expired() const
{
	return this->_Rep->_Uses == 0;
}

9.锁

my_shared_ptr<_Ty> lock() const
{
	my_shared_ptr<_Ty> _Ret;
	_Ret._Ptr = _Rep->_Ptr;
	_Ret._Rep = _Rep;
	_Ret._Rep->_Incref();
	return _Ret;
}

运行示例

void fun()
{
	my_shared_ptr<Object> op1(new Object(10));
	my_weak_ptr<Object> wp1(op1);
	my_weak_ptr<Object> wp2(op1);
}
int main()
{
	fun();
	return 0;
}

在这里插入图片描述

完整代码

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

template<class _Ty>
class MyDeletor
{
public:
	MyDeletor() = default;
	void operator()(_Ty* ptr) const
	{
		if (ptr != nullptr)
		{
			delete ptr;// one 
		}
	}
};

template<class _Ty>
class MyDeletor<_Ty[]>
{
public:
	MyDeletor() = default;
	void operator()(_Ty* ptr) const
	{
		if (ptr != nullptr)
		{
			delete[]ptr; // array
		}
	}
};

template<class _Ty>
class RefCnt
{
public:
	_Ty* _Ptr;               // Obj;
	std::atomic_int _Uses;   // shared_ptr;
	std::atomic_int _Weaks;  // weak_ptr;

public:
	RefCnt(_Ty* p) :_Ptr(p), _Uses(1), _Weaks(1) {}
	~RefCnt() {}
	void _Incref() { _Uses += 1; }
	void _Incwref() { _Weaks += 1; }
};

template<class _Ty> class my_weak_ptr;

template<class _Ty, class _Dx = MyDeletor<_Ty> >
class my_shared_ptr // thread;
{
private:
	_Ty*           _Ptr; // Object;
	RefCnt<_Ty>  * _Rep;
	_Dx _mDeletor;
public:
	my_shared_ptr(_Ty* p = nullptr) :_Ptr(nullptr), _Rep(nullptr)
	{
		if (p != nullptr)
		{
			_Ptr = p;
			_Rep = new RefCnt<_Ty>(p);
		}
	}
	my_shared_ptr(const my_shared_ptr& _Y):_Ptr(_Y._Ptr),_Rep(_Y._Rep)
	{
		if (_Rep != nullptr)
		{
			_Rep->_Incref(); // _Uses
		}
	}
	my_shared_ptr(my_shared_ptr&& other):_Ptr(other._Ptr),_Rep(other._Rep)
	{
		other._Ptr = nullptr;
		other._Rep = nullptr;
	}
	my_shared_ptr& operator=(const my_shared_ptr& r)
	{
		if (this == &r || this->_Ptr == r._Ptr) return *this;
		if (_Ptr != nullptr && --_Rep->_Uses == 0)
		{
			_mDeletor(_Ptr);
			if (--_Rep->_Weaks == 0)
			{
				delete _Rep;
			}
		}
		_Ptr = r._Ptr;
		_Rep = r._Rep;
		if (_Ptr != nullptr)
		{
			_Rep->_Incref(); // _Uses;
		}
		return *this;
	}
	my_shared_ptr& operator=(my_shared_ptr&& other)
	{
		if (this == &other) return *this;
		if (_Ptr != nullptr && other._Ptr != nullptr && _Ptr == other._Ptr)
		{
			this->_Rep->_Uses -= 1;
			other._Ptr = nullptr;
			other._Rep = nullptr;
			return *this;
		}

		if (_Ptr != nullptr && --_Rep->_Uses == 0)
		{
			_mDeletor(_Ptr);
			if (--_Rep->_Weaks == 0)
			{
				delete _Rep;
			}
		}
		_Ptr = other._Ptr;
		_Rep = other._Rep;
		other._Ptr = nullptr;
		other._Rep = nullptr;
		return *this;
	}
	~my_shared_ptr()
	{
		if (_Rep != nullptr && --_Rep->_Uses == 0)
		{
			_mDeletor(_Ptr);
			if (--_Rep->_Weaks == 0)
			{
				delete _Rep;
			}
		}
		_Ptr = nullptr;
		_Rep = nullptr;
	}

	_Ty* get() const { return _Ptr; }
	_Ty& operator*() const { return *get(); }
	_Ty* operator->() const { return get(); }
	size_t use_count() const
	{
		if (_Rep == nullptr) return 0;
		return _Rep->_Uses;
	}
	void swap(my_shared_ptr& r)
	{
		std::swap(_Ptr, r._Ptr);
		std::swap(_Rep, r._Rep);
	}
	operator bool() const { return _Ptr != nullptr; }

	template<class _Ty>
	friend class my_weak_ptr;
};

template<class _Ty>
class my_weak_ptr
{
private:
	RefCnt<_Ty>* _Rep;
public:
	my_weak_ptr() :_Rep(nullptr) {}
	my_weak_ptr(const my_shared_ptr<_Ty>& other):_Rep(other._Rep)
	{
		if (_Rep != nullptr)
		{
			_Rep->_Incwref(); // Weaks;
		}
	}
	my_weak_ptr(const my_weak_ptr& other):_Rep(other._Rep)
	{
		if (_Rep != nullptr)
		{
			_Rep->_Incwref();
		}
	}
	my_weak_ptr(my_weak_ptr&& other):_Rep(other._Rep)
	{
		other._Rep = nullptr;
	}
	my_weak_ptr& operator=(const my_weak_ptr& other)
	{
		if (this == &other || this->_Rep == other._Rep) return *this;
		if (_Rep != nullptr && --_Rep->_Weaks == 0)
		{
			delete _Rep;
		}
		_Rep = other->_Rep;
		if (_Rep != nullptr)
		{
			_Rep->_Incwref();
		}
		return *this;
	}
	my_weak_ptr& operator=(my_weak_ptr&& other)
	{
		if (this == &other) return *this;
		if (this->_Rep != nullptr && other._Rep != nullptr && _Rep == other._Rep)
		{
			this->_Rep->_Weaks -= 1;
			other._Rep = nullptr;
			return *this;
		}
		if (_Rep != nullptr && --_Rep->_Weaks == 0)
		{
			delete _Rep;
		}
		_Rep = other._Rep;
		other._Rep = nullptr;
		return *this;
	}
	my_weak_ptr& operator=(const my_shared_ptr<_Ty>& other)
	{
		if (_Rep != nullptr && --_Rep->_Weaks == 0)
		{
			delete _Rep;
		}
		_Rep = other._Rep;
		if (_Rep != nullptr)
		{
			_Rep->_Incwref();
		}
		return *this;
	}
	my_weak_ptr& operator=(my_shared_ptr<_Ty>&& other) = delete;

	~my_weak_ptr()
	{
		if (_Rep != nullptr && --_Rep->_Weaks == 0)
		{
			delete _Rep;
		}
		_Rep = nullptr;
	}
	bool expired() const
	{
		return this->_Rep->_Uses == 0;
	}
	my_shared_ptr<_Ty> lock() const
	{
		my_shared_ptr<_Ty> _Ret;
		_Ret._Ptr = _Rep->_Ptr;
		_Ret._Rep = _Rep;
		_Ret._Rep->_Incref();
		return _Ret;
	}
};

class Object
{
private:
	int value;
public: 
	Object(int x = 0) :value(x) { cout << "Obejct" << endl; }
	~Object() { cout << "~Object:" << endl; }

	void Print() const { cout << "value: " << value << endl; }
};
void fun()
{
	my_shared_ptr<Object> op1(new Object(10));
	my_weak_ptr<Object> wp1(op1);
	my_weak_ptr<Object> wp2(op1);
}
int main()
{
	fun();
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_索伦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值