C++智能指针 shared_ptr 模仿实现

在之前的文章中介绍了智能指针的应用场景, 并简易实现了 shared_ptr 和 unique_ptr 两种智能指针, 但是还不够全面,比如不支持注册资源释放函数、不支持多线程等。本文就来实现一个相对完整的 shared_ptr 智能指针,毕竟 unique_ptr 比它要简洁,实现了 shared_ptr 后再看 unique_ptr 就容易了。

完整代码地址:https://github.com/pengguoqing/samples_code/tree/master/c%2B%2B/shared_ptr

一、引用计数

  引用计数类的实现在之前的文章中已经详细介绍了,shared_ptr 就是用它来支撑多线程的,这里直接放上链接:引用计数类实现

二、CEXSharedPtr实现细节

2.1 CEXSharedPtr 数据成员

  shared_ptr 数据成员至少应该有两个,一个是指向资源的指针,另外一个指向引用计数的指针。除此之外,用户还可以自定义资源释放,所以最多三个数据成员就可以了。 C++ 标准库里面的实现只有数据指针和控制块指针, 就是它把 引用计数,析构器和弱引用封装到了一起,本文的实现就不封装了。

private:
	CEXRefCounter* m_ref;
	T*			   m_data;
	Function       m_deleter;

2. 2 CEXSharedPtr构造和析构函数

  构造函数包括无参构造,有参构造,拷贝构造和移动构造,需要注意的地方就是注册析构器时需要用到万能引用(转发应用), 实现细如下:

inline CEXSharedPtr()
			:m_ref(nullptr),
			 m_data(nullptr) 
	{
    
    }

	inline CEXSharedPtr(T* ptr)
			:m_ref(ptr ? new CEXRefCounter(1) : nullptr),
			 m_data(ptr),
			 m_deleter(Destutct())
	{
    
    }

	inline CEXSharedPtr(T* ptr, CEXRefCounter* ref)
		: m_ref(ref),
		  m_data(ptr),
		  m_deleter(Destutct())
	{
    
    
		if (m_ref) m_ref->AddRef();
	}

	//注册类似 lambda表达式 的仿函数析构器
	template<class T, class LambdaDestruct>
	inline CEXSharedPtr(T* ptr, LambdaDestruct&& lambda)
			:m_ref(ptr ? new CEXRefCounter(1) : nullptr),
			 m_data(ptr),
		     m_deleter(lambda)
	{
    
    }
	
	inline CEXSharedPtr(const CEXSharedPtr& another)
			:m_data(another.m_data),
			 m_ref(another.m_ref),
			 m_deleter(another.m_deleter)
	{
    
    
		     if (m_ref) m_ref->AddRef();
	}

	inline CEXSharedPtr(CEXSharedPtr&& another) noexcept
		   :m_data(another.m_data),
			m_ref(another.m_ref),
			m_deleter(another.m_deleter)
	{
    
    
		another.m_ref  = nullptr;
		another.m_data = nullptr;
	}

  析构函数的逻辑是将引用计数减一, 当发现引用计数为 0 时则调用析构器释放资源,提供两种析构器,分别用于释放指针和数组指针。

template<class T>
struct CXRelease
{
    
    
	void operator()(T* data) noexcept
	{
    
    
		delete data;
	}
};

template<class T>
struct CXReleaseArray
{
    
    
	void operator()(T* data) noexcept
	{
    
    
		delete[] data;
	}
};

  资源释放函数为和析构函数为:

inline ~CEXSharedPtr()
	{
    
    
		Release();
	}

private:
	void Release()
	{
    
    
		if (m_ref &&  0 >= m_ref->Release())
		{
    
    
			m_deleter(m_data);
			m_data = nullptr;
			
			delete m_ref;
			m_ref = nullptr;	
		}
	}	

2. 3 CEXSharedPtr赋值运算符重载, 重置和赋值函数

 包括移动赋值,拷贝赋值和指针赋值。

	CEXSharedPtr& operator = (const CEXSharedPtr& another)
	{
    
    
		return Assign(another);
	}

	CEXSharedPtr& operator = (T* data)
	{
    
    
		return Assign(data);
	}

	CEXSharedPtr& operator = (CEXSharedPtr&& another) noexcept
	{
    
    
		Swap(another);
		another.Reset();
        return *this;
	}
	
	CEXSharedPtr& Assign(T* data)
	{
    
    
		if (Get() != data)
		{
    
    
			CEXSharedPtr tmp(data);
			Swap(tmp);
		}

		return *this;
	}
	
	CEXSharedPtr& Assign(const CEXSharedPtr& another)
	{
    
    
		if (&another != this))
		{
    
    
			CEXSharedPtr tmp(another);
			Swap(tmp);
		}

		return *this;
	}

	void Reset()
	{
    
    
		Assign(nullptr);
	}

	void Reset(T* data)
	{
    
    
		Assign(data);
	}

	void Reset(const CEXSharedPtr& another)
	{
    
    
		Assign(another);
	}

2. 4 CEXSharedPtr指针相关运算符

  CEXSharedPtr对象当指针来使用时常用的操作符重载,以及逻辑判断中的类型转换。

	T* Get() const noexcept
	{
    
    
		return m_data;
	}

	T* operator -> () const noexcept
	{
    
    
		return m_data;
	}

	T& operator * () const noexcept
	{
    
    
		 return  *m_data;
	}

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

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

	bool operator == (const T* data) const
	{
    
    
		return Get() == data;
	}

	bool operator == (const CEXSharedPtr& another) const
	{
    
    
		return Get() == another->Get();
	}

	int Count() const
	{
    
    
		return m_ref ? m_ref->value() : 0;
	}

2. 5 CEXSharedPtr 对象创建函数

 主要主要模仿 C++ 标准库的 make_shared,实现类似的功能。

template <class T, class... Args>
CEXSharedPtr<T> MakeShared(Args&&... args)
{
    
    
	return CEXSharedPtr<T>(new T{
    
    std::forward<Args>(args)...});
}

template <class T>
CEXSharedPtr<T> MakeArrayShared(int size)
{
    
    
	return CEXSharedPtr<T>(new T[size], CXReleaseArray<T>());
}

三、部分测试代码

 简要测试代码和运行结果如下:

    CEXSharedPtr<int> ptr(new int{
    
    0});
    std::cout<< "ptr" <<"count: "<<ptr.Count()<<"  data: "<<*ptr<<std::endl;
    
    CEXSharedPtr<int> ptr1(new int{
    
    1}, [](int* ptr){
    
    delete ptr;});
    std::cout << "ptr1" << "count: " << ptr1.Count() << "  data: " << *ptr1 << std::endl;
    
    std::function<void(int*)> deleter(CustomDeleter);
    CEXSharedPtr<int> ptr2(new int{
    
    2}, deleter);
    std::cout << "ptr2" << "count: " << ptr2.Count() << "  data: " << *ptr2 << std::endl;

    CEXSharedPtr<int> ptr3(std::move(ptr2));
    ptr2.Count();
    std::cout << "ptr3" << "count: " << ptr3.Count() << "  data: " << *ptr3 << std::endl;

    CEXSharedPtr<int> ptr4(ptr3);
    std::cout << "ptr4" << "count: " << ptr4.Count() << "  data: " << *ptr4 << std::endl;

    CEXSharedPtr<CXSuper> superPtr(new CXSub);
    CEXSharedPtr<CXSub> subPtr(superPtr.Cast<CXSub>());

    CEXSharedPtr<CXMembers> initListPtr = MakeShared<CXMembers>('I', 1, .5f);

    CEXSharedPtr<CXMembers> arrayPtr =    MakeArrayShared<CXMembers>(5);
    std::cout << "Hello World!\n";

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/PX1525813502/article/details/128211863