实现简单的shared_ptr

类图如下:
在这里插入图片描述

shared_ptr和weak_ptr都有一个基类Ptr_base,Ptr_base有指向资源的指针_Ptr和指向引用计数的指针_Rep

引用计数对象Ref_count封装了对资源以及引用计数对象的释放操作,Ref_count也有一个基类Ref_count_base,封装了对引用计数_Uses、_Weaks,以及对_Uses、_Weaks的++、--操作

My_Ref_count_base

My_Ref_count_base类是一个抽象类:

  • 有两个计数器变量:_Uses供shared_ptr计数用,_Weaks供weak_ptr指针计数用
  • 保证线程安全:_Uses和_Weaks加1减1使用原子操作
  • 如果_Uses值为0,删除资源对象,调用_Destroy()
  • 如果_Weaks的值为0,删除引用计数器对象,也就是删除自己,调用Delete_this()
  • 两个纯虚函数:_Destroy()删除资源对象;Delete_this()删除引用计数器对象本身
class My_Ref_count_base {
	public:
		// 派生类无法进行拷贝构造和赋值
		My_Ref_count_base(const My_Ref_count_base&) = delete;
		My_Ref_count_base& operator=(const My_Ref_count_base&) = delete;
		// 虚析构,多态释放
		virtual ~My_Ref_count_base(){};

		void _Incref() { _Uses += 1; }
		void _Incwref() { _Weaks += 1; }
		
		void _Decref() {
			if (--_Uses == 0) {
				// _Uses == 0,删除资源,并将_Weaks--
				_Destroy();
				_Decwref();
			}
		}
		
		void _Decwref() {
			if (--_Weaks == 0) {
				_Delete_this();
			}
		}

		long _use_count() const {
			return _Uses;
		}

	private:
		virtual void _Destroy() = 0;      // 删除资源对象
		virtual void _Delete_this() = 0;  // 删除引用计数器对象本身

		std::atomic<int> _Uses{ 1 };
		std::atomic<int> _Weaks{ 1 };

	protected:
		My_Ref_count_base() = default;
};

My_Ref_count

My_Ref_count_base的第一个派生类是My_Ref_count,无删除器的引用计数类型,它是能够实例化对象的引用计数类型,使用的时候都是用的My_Ref_count,只有把指针类型声明成My_Ref_count_base

  • 数据成员_Ptr,指向资源对象
  • 实现了My_Ref_count_base抽象基类中的纯虚函数
template<class T>
class My_Ref_count : public My_Ref_count_base{
public:
	My_Ref_count(T* Px)
		: My_Ref_count_base()
		, _Ptr(Px)
	{}
private:
	// 删除资源对象
	virtual void _Destroy() {
		delete _Ptr;
	}

	// 删除引用计数器对象本身
	virtual void _Delete_this() {
		delete this;
	}
private:
	T* _Ptr; // 指向资源对象
};

My_Ptr_base

该类是My_shared_ptr和My_weak_ptr的共同基类

  • 有两个指针成员,分别是element_type* _Ptr和My_Ref_count_base * _Rep
  • _Ptr指向资源地址,_Rep指向引用计数器对象
template<class T>
class My_Ptr_base {
public:
	using element_type = T;
	My_Ptr_base(const My_Ptr_base&) = delete;
	My_Ptr_base& operator=(const My_Ptr_base&) = delete;

	element_type* get() const {
		return _Ptr;
	}

	long use_count() const {
		return _Rep == nullptr ? 0 : _Rep->_use_count();
	}

protected:
	My_Ptr_base() = default;
	~My_Ptr_base() = default;

	// const My_Ptr_base* this
	void Incref() const {
		if (nullptr != _Rep) {
			_Rep->_Incref();
		}
	}

	void Inwcref() const {
		if (nullptr != _Rep) {
			_Rep->_Incwref();
		}
	}

	void Decref() const {
		if (nullptr != _Rep) {
			_Rep->_Decref();
		}
	}

	void Decwref() const {
		if (nullptr != _Rep) {
			_Rep->_Decwref();
		}
	}

	void Swap(My_Ptr_base& right) {
		std::swap(_Ptr, right._Ptr);
		std::swap(_Rep, right._Rep);
	}
	
	void _Copy_construct_from(const My_shared_ptr<T>& left) {
		if (this == &left) {
			return;
		}
		this->_Ptr = left._Ptr;
		this->_Rep = left._Rep;
		this->Incref();
	}

	// 移动构造的参数不要用const,否则不能修改参数的成员
	void _Move_construct_from(My_shared_ptr<T>&& right) {
		if (this == &right) {
			return;
		}
		this->_Ptr = right._Ptr;
		this->_Rep = right._Rep;
		right._Ptr = nullptr;
		right._Rep = nullptr;
	}
protected:
	element_type* _Ptr{nullptr};
	My_Ref_count_base* _Rep{ nullptr };
};

My_shared_ptr

template<class T>
class My_shared_ptr : public My_Ptr_base<T> {
public:
	explicit My_shared_ptr() = default;
	// explicit My_shared_ptr(nullptr_t) = default;
	explicit My_shared_ptr(T * Px) {
		this->_Ptr = Px;                              // 资源
		this->_Rep = new My_Ref_count<T>(Px);         // 引用计数器
	}

	// My_shared_ptr的拷贝构造,就是需要将资源指针_Ptr和引用计数_Rep指针赋值,然后引用计数++
	My_shared_ptr(const My_shared_ptr<T>& left) {
		this->_Copy_construct_from(left);
	}

	My_shared_ptr(My_shared_ptr<T>&& right) {
		this->_Move_construct_from(std::move(right)); // right为左值引用变量,本身是一个左值,需要再转换一次
	}

	~My_shared_ptr() {
		this->Decref();
	}

	void swap(My_shared_ptr& right) {
		this->Swap(right);
	}
	// 将当前对象置空
	void reset() {
		My_shared_ptr().swap(*this);    // swap后,this有了临时对象的资源,即被置空了,临时对象有了this的资源,出作用域被析构
	}

	void reset(T* Px) {
		My_shared_ptr(Px).swap(*this);
	}

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

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


private:
	using My_Ptr_base<T>::get;
};

完整代码:


class My_Ref_count_base;

template<class T>
class My_Ref_count;

template<class Resource, class Dx>
class My_Ref_count_resource;

template<class T>
class My_Ref_base;

template<class T>
class My_shared_ptr;

template<class T>
class My_weak_ptr;

// 引用计数对象的基类
class My_Ref_count_base {
	public:
		// 派生类无法进行拷贝构造和赋值
		My_Ref_count_base(const My_Ref_count_base&) = delete;
		My_Ref_count_base& operator=(const My_Ref_count_base&) = delete;
		virtual ~My_Ref_count_base() {};        // 虚析构,多态释放

		void _Incref() { _Uses += 1; }
		void _Incwref() { _Weaks += 1; }

		void _Decwref() {
			if (--_Weaks == 0) {
				_Delete_this();
			}
		}

		void _Decref() {
			if (--_Uses == 0) {
				// _Uses == 0,删除资源,并将_Weaks--
				_Destroy();
				_Decwref();
			}
		}

		long _use_count() const {
			return _Uses;
		}

	private:
		virtual void _Destroy() = 0;      // 删除资源对象
		virtual void _Delete_this() = 0;  // 删除引用计数器对象本身

		std::atomic_int _Uses{ 1 };
		std::atomic_int _Weaks{ 1 };

	protected:
		My_Ref_count_base() = default;
};

// 无删除器的引用计数类型
template<class T>
class My_Ref_count : public My_Ref_count_base{
public:
	My_Ref_count(T* Px)
		: My_Ref_count_base()
		, _Ptr(Px)
	{}
private:
	// 删除资源对象
	virtual void _Destroy() {
		delete _Ptr;    // 删除单个对象,而不是一组对象
	}

	// 删除引用计数器对象本身
	virtual void _Delete_this() {
		delete this;
	}
private:
	T* _Ptr;
};

template<class T>
class My_Ptr_base {
public:
	using element_type = T;

	My_Ptr_base(const My_Ptr_base&) = delete;
	My_Ptr_base& operator=(const My_Ptr_base&) = delete;

	element_type* get() const {
		return _Ptr;
	}

	long use_count() const {
		return _Rep == nullptr ? 0 : _Rep->_use_count();
	}

protected:
	My_Ptr_base() = default;
	~My_Ptr_base() = default;

	// const My_Ptr_base* this
	void Incref() const {
		if (nullptr != _Rep) {
			_Rep->_Incref();
		}
	}

	void Inwcref() const {
		if (nullptr != _Rep) {
			_Rep->_Incwref();
		}
	}

	void Decref() const {
		if (nullptr != _Rep) {
			_Rep->_Decref();
		}
	}

	void Decwref() const {
		if (nullptr != _Rep) {
			_Rep->_Decwref();
		}
	}

	void Ptr_base_Swap(My_Ptr_base& right) {
		std::swap(_Ptr, right._Ptr);
		std::swap(_Rep, right._Rep);
	}

protected:
	void _Copy_construct_from(const My_shared_ptr<T>& left) {
		if (this == &left) {
			return;
		}
		this->_Ptr = left._Ptr;
		this->_Rep = left._Rep;
		this->Incref();
	}

	// 因为weak_ptr可以通过weak_ptr和shared_ptr进行拷贝构造,所有这里形参用My_Ptr_base,可以接收weak_ptr和shared_ptr实参
	void _Weak_Copy_construct_from(const My_Ptr_base<T>& left) {
		if (this == &left) {
			return;
		}
		// weak_ptr只关心引用计数器是否存活
		if (nullptr != left._Rep) {
			this->_Ptr = left._Ptr;
			this->_Rep = left._Rep;
			this->Inwcref();
		}
	}

	// 移动构造的参数不要用const,否则不能修改参数的成员
	void _Move_construct_from(My_Ptr_base<T>&& right) {
		if (this == &right) {
			return;
		}
		this->_Ptr = right._Ptr;
		this->_Rep = right._Rep;
		right._Ptr = nullptr;
		right._Rep = nullptr;
	}

protected:
	element_type* _Ptr{nullptr};
	My_Ref_count_base* _Rep{ nullptr };
};

template<class T>
class My_shared_ptr : public My_Ptr_base<T> {
public:
	explicit My_shared_ptr() = default;
	// explicit My_shared_ptr(nullptr_t) = default;
	explicit My_shared_ptr(T * Px) {
		this->_Ptr = Px;                              // 资源
		this->_Rep = new My_Ref_count<T>(Px);         // 引用计数器
	}

	// My_shared_ptr的拷贝构造,就是需要将资源指针_Ptr和引用计数_Rep指针赋值,然后引用计数++
	My_shared_ptr(const My_shared_ptr<T>& left) {
		this->_Copy_construct_from(left);
	}

	My_shared_ptr(My_shared_ptr<T>&& right) {
		this->_Move_construct_from(std::move(right)); // right为左值引用变量,本身是一个左值,需要再转换一次
	}

	My_shared_ptr(const My_weak_ptr<T>& left) {
		this->_Copy_construct_from(left);
	}

	~My_shared_ptr() {
		this->Decref();
	}

	void swap(My_shared_ptr& right) {
		this->Swap(right);
	}
	// 将当前对象置空
	void reset() {
		My_shared_ptr().swap(*this);                // swap后,this有了临时对象的资源,即被置空了,临时对象有了this的资源,出作用域被析构
	}

	void reset(T* Px) {
		My_shared_ptr(Px).swap(*this);
	}

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

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


private:
	using My_Ptr_base<T>::get;
};

直接使用构造函数和使用make_shared的区别

在这里插入图片描述
手动调用new缺陷: 如果 shared_ptr sp(new int(10)) 这行代码中的new int(10) 执行成功了,而shared_ptr的构造函数执行失败了,就不会有引用计数的_Ref_count对象,就意味着不存在shared_ptr对象,就不会调用shared_ptr的析构函数,那我们new出来的堆区资源也就不会释放了

手动调用new好处: 如果我们手动调用构造函数生成shared_ptr对象,资源和引用计数对象是分开存放在堆区的,当Uses_为0,Weaks_不为0时,会把资源释放,而不释放引用计数对象,即相对于使用make_shared资源对象可以提前释放。只有Uses_和Weaks_同时为0,引用计数对象才会被释放。

在这里插入图片描述
make_shared优点:

  • make_shared把资源和引用计数的对象放在连续的空间中,只需要new一次,解决了上面的问题。new失败没有资源泄露,new成功析构函数会释放资源
  • 根据程序运行的局部性原理,将资源对象和引用计数器对象放在一起比分开放,能减少一半的cache miss

make_shared缺点:

  • 由于使用make_shared分配的资源空间和引用计数对象的空间是连续的,是一次性申请的,也需要一次性释放。所以对于make_shared new出来的内存,就算引用计数_Uses为0,而_Weaks不为0,无论是int(10)的空间还是引用计数对象空间都不能释放,因为_Weaks不为0,引用计数对象空间不能释放,整块资源都不能释放

  • 当资源对象很大时,Uses_为0,也不能及时释放,会占用系统空间。而使用make_shared可以减少内存分配的次数,且能防止了资源泄露

  • 无法自定义删除器,默认析构函数是delete,无法管理打开的文件。如果想自定义删除器,还得手动调用构造函数

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
shared_ptrC++中用于动态内存管理的智能指针之一。它能够记录对象被引用的次数,主要用于管理动态创建的对象的销毁。使用shared_ptr可以避免内存泄漏和悬挂指针等问题。 在C++中,实现shared_ptr通常需要以下几个步骤: 1. 定义一个模板类,例如SHARED_ptr,该类将作为智能指针使用。 2. 在SHARED_ptr类中,定义一个指针成员变量ptr,用于指向实际的对象。 3. 在SHARED_ptr类中,定义一个计数器类的指针成员变量refcount,用于记录对象被引用的次数。 4. 在SHARED_ptr类中,实现构造函数,用于初始化指针和计数器。 5. 在SHARED_ptr类中,实现拷贝构造函数和赋值操作符重载,用于处理多个智能指针共享同一对象的情况。 6. 在SHARED_ptr类中,实现析构函数,用于释放对象的内存空间。 7. 在SHARED_ptr类中,实现箭头运算符重载和解引用运算符重载,用于访问对象的成员函数和数据。 8. 在SHARED_ptr类外部,实现计数器类RefCount,用于记录对象被引用的次数,并在引用次数为0时释放对象的内存空间。 实现shared_ptr的详细代码如下所示: ```cpp template <class T> class SHARED_ptr { private: T* ptr; // 用来指向堆区对象 RefCount<T>* refcount; // 指向计数器对象的指针 public: SHARED_ptr(T* p) : ptr(p), refcount(new RefCount<T>()) { refcount->increment(); } SHARED_ptr(const SHARED_ptr<T>& other) : ptr(other.ptr), refcount(other.refcount) { refcount->increment(); } ~SHARED_ptr() { if (refcount->decrement() == 0) { delete ptr; delete refcount; } } T* operator->() const { return ptr; } T& operator*() const { return *ptr; } SHARED_ptr& operator=(const SHARED_ptr<T>& other) { if (this != &other) { if (refcount->decrement() == 0) { delete ptr; delete refcount; } ptr = other.ptr; refcount = other.refcount; refcount->increment(); } return *this; } }; ``` 以上是一个简单C++实现shared_ptr的示例代码。通过使用shared_ptr,我们可以方便地管理动态创建的对象的生命周期,并避免内存泄漏和悬挂指针等问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bugcoder-9905

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

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

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

打赏作者

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

抵扣说明:

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

余额充值