智能指针的模拟实现shared_ptr 循环引用 定置删除器

本文探讨了智能指针shared_ptr的实现原理,通过引用计数管理内存,但这种方式存在线程安全、循环引用及定置删除器等问题。针对循环引用,介绍了弱引用智能指针weak_ptr的解决方案。同时,提到了使用仿函数处理特定场景下的空间释放,如文件关闭或malloc分配的空间使用free释放。
摘要由CSDN通过智能技术生成

auto_ptr与scoped_ptr的实现见本人的上篇博客。

三、shared_ptr

shared_ptr的实现原理是通过引用计数来实现,只有当引用计数为1时才释放空间,否则只需将引用计数减1.拷贝和赋值将引用计数加1,具体代码如下:

template <typename T>
class SharedPtr
{
public:
	SharedPtr();
	SharedPtr(T* ptr);
	SharedPtr(const SharedPtr<T>& ap);
	~SharedPtr();
	//SharedPtr<T>& operator=(const SharedPtr<T>& ptr);//传统写法
	SharedPtr<T>& operator=(SharedPtr<T> ap);//现代写法
	T& operator*()const;
	T* operator->()const;
	long GetCount()const;
	T* GetPtr()const;
protected:
	void _Realease();
protected:
	T* _ptr;
	long* _pCount;
};
template <typename T>
SharedPtr<T>::SharedPtr() :_ptr(NULL), _pCount(new long(1))
{}
template <typename T>
SharedPtr<T>::SharedPtr(T* ptr) : _ptr(ptr), _pCount(new long(1))
{}
template <typename T>
SharedPtr<T>::SharedPtr(const SharedPtr<T>& ap) : _ptr(ap._ptr), _pCount(ap._pCount)
{
	++(*this->_pCount);
}
template <typename T>
SharedPtr<T>::~SharedPtr()
{
	this->_Realease();
}
//template <typename T>//传统写法
//SharedPtr<T>& SharedPtr<T>::operator=(const SharedPtr<T>& ap)
//{
//	if (this->_ptr != ap._ptr)
//	{
//		this->_Realease();
//		this->_ptr = ap._ptr;
//		this->_pCount = ap._pCount;
//		++(*this->_pCount);
//	}
//	return *this;
//}
template <typename T>//现代写法
SharedPtr<T>& SharedPtr<T>::operator=(SharedPtr<T> ap)
{
	swap(this->_ptr, ap._ptr);
	swap(this->_pCount, ap._pCount);
	return *this;
}
template <typename T>
T& SharedPtr<T>::operator*()const
{
	return *(this->_ptr);
}
template <typename T>
T* SharedPtr<T>::operator->()const
{
	return this->_ptr;
}
template <typename T>
long SharedPtr<T>::GetCount()const
{
	return *(this->_pCount);
}
template <typename T>
T* SharedPtr<T>::GetPtr()const
{
	return this->_ptr;
}
template <typename T>
void SharedPtr<T>::_Realease()
{
	if (--(*this->_pCount) == 0)
	{
		delete this->_ptr;
		delete this->_pCount;
	}
}

然而上面用引用计数实现的简化版看起来不错,但却存在以下问题:

1、引用计数更新存在着线程安全

2、循环引用

3、定置删除器,比如要关闭一个文件,用malloc开辟出来的空间,上述代码均会出现问题

问题1的解决需要对改变引用计数时加锁。我们暂时不讨论,一下我们主要看第二个和第三个问题。

循环引用:

比如有以下结构体和主函数:

struct ListNode
{
	shared_ptr<ListNode> _prev; //shared_ptr为库文件中实现的,只需包memory即可使用
	shared_ptr<ListNode> _next;
	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};
int main()
{
	shared_ptr<ListNode> prev; //语句1
	shared_ptr<ListNode> next; //语句2
	prev->_next = next;        //语句3
	next->_prev = prev;        //语句4
}

经语句1、2之后prev的引用计数为1,经3、4后为2,但是最后两个对象均不能释放,因为prev的要释放的前提是next释放,而next的释放又依赖于prev的释放。最后就形成了循环引用,谁都是放不了。解决方案如下:

struct ListNode
{
	weak_ptr<ListNode> _prev;
	weak_ptr<ListNode> _next;
	~ListNode()
	{
		cout << "~ListNode()" << endl;
	}
};

因为weak_ptr(弱引用智能指针)会对引用计数会做特殊处理(上述情况不加1)。

定置删除器和空间分配器(ps:空间分配器的定置特殊场景下才会这样使用)

eg:如果指针是一个指向文件类型的,在析构函数中只需关闭文件即可,而不是释放空间;如果空间是通过,malloc出来的,那么在析构函数中要调用free函数,而不是delete操作符。上述问题通过仿函数就可以解决。具体代码如下:

template <typename T,class Deleter = Del<T>>
class SharedPtr
{
public:
	SharedPtr(T* ptr);
	SharedPtr(T* ptr, Deleter del);
	SharedPtr(const SharedPtr<T, Deleter>& ap);
	~SharedPtr();
	//SharedPtr<T, Deleter>& operator=(const SharedPtr<T, Deleter>& ptr);//传统写法
	//现代写法
	SharedPtr<T, Deleter>& operator=(SharedPtr<T, Deleter> ap);
	T& operator*()const;
	T* operator->()const;
	long GetCount()const;
	T* GetPtr()const;
protected:
	void _Realease();
protected:
	T* _ptr;
	long* _pCount;
	Deleter _del;
};

template <typename T, class Deleter = Del<T>>
SharedPtr<T, Deleter>::SharedPtr(T* ptr) :_ptr(ptr), _pCount(new long(1))
{}

template <typename T, class Deleter = Del<T>>
SharedPtr<T, Deleter>::SharedPtr(T* ptr, Deleter del) : _ptr(ptr), _pCount(new long(1)), _del(del)
{}

template <typename T, class Deleter = Del<T>>
SharedPtr<T, Deleter>::SharedPtr(const SharedPtr<T, Deleter>& ap) : _ptr(ap._ptr), _pCount(ap._pCount), _del(ap._del)
{
	++(*this->_pCount);
}

template <typename T, class Deleter = Del<T>>
SharedPtr<T, Deleter>::~SharedPtr()
{
	this->_Realease();
}

//template <typename T, class Deleter = Del<T>>//传统写法
//SharedPtr<T, Deleter>& SharedPtr<T, Deleter>::operator=(SharedPtr<T, Deleter> ap)
//{
//	if (this->_ptr != ap._ptr)
//	{
//		this->_Realease();
//		this->_ptr = ap._ptr;
//		this->_pCount = ap._pCount;
//		++(*this->_pCount);
//	}
//	return *this;
//}

template <typename T, class Deleter = Del<T>>//现代写法
SharedPtr<T, Deleter>& SharedPtr<T, Deleter>::operator=(SharedPtr<T, Deleter> ap)
{
	swap(this->_ptr, ap._ptr);
	swap(this->_pCount, ap._pCount);
	swap(this->_del, ap._del);
	return *this;
}

template <typename T, class Deleter = Del<T>>
T& SharedPtr<T, Deleter>::operator*()const
{
	return *(this->_ptr);
}

template <typename T, class Deleter = Del<T>>
T* SharedPtr<T, Deleter>::operator->()const
{
	return this->_ptr;
}

template <typename T, class Deleter = Del<T>>
long SharedPtr<T, Deleter>::GetCount()const
{
	return *(this->_pCount);
}

template <typename T, class Deleter = Del<T>>
T* SharedPtr<T, Deleter>::GetPtr()const
{
	return this->_ptr;
}

template <typename T, class Deleter = Del<T>>
void SharedPtr<T, Deleter>::_Realease()
{
	if (--(*this->_pCount) == 0)
	{
		_del(_ptr);
		delete this->_pCount;
	}
}
### 回答1: Boost库中的shared_ptr是一个智能指针,用于管理动态分配的对象。要用C语言实现shared_ptr,需要实现以下功能: 1. 一个结构体用于存储指向动态分配对象的指针以及计数。 2. 函数用于创建一个新的共享指针,增加计数,并将结构体指针返回。 3. 函数用于销毁一个共享指针,减少计数,并在计数为0时释放指向动态分配对象的指针。 需要注意的是,C语言没有自动垃圾回收机制,因此需要手动管理内存。此外,使用shared_ptr时要小心避免出现循环引用的问题。 总体来说,用C语言实现shared_ptr是一项复杂的任务,需要对内存管理和指针操作有深入的理解。建议使用现成的智能指针实现,如std::shared_ptr。 ### 回答2: 在C语言中实现Boost库的shared_ptr是一项相当有挑战性的任务,因为Boost库是使用C++编写的,利用其丰富的语言特性来实现shared_ptr智能指针功能。 为了实现类似的智能指针功能,我们可以使用C语言中的结构体和函数指针来模拟类和成员函数的概念。 首先,我们可以创建一个名为shared_ptr的结构体,其中包含一个指向资源的指针和一个指向资源的计数指针。计数用于跟踪当前资源被多少shared_ptr对象共享。 接下来,我们创建一系列的操作函数,例如构造函数、析构函数、拷贝构造函数和赋值运算符等。 在构造函数中,我们需要初始化shared_ptr对象,并将资源指针和计数指针分配内存。同时,我们需要将计数的初始值设置为1。 在析构函数中,我们需要释放资源指针并减少计数的值。当计数为0时,表示资源没有被任何shared_ptr对象所引用,我们需要释放计数指针。 在拷贝构造函数和赋值运算符中,我们需要增加计数的值,并在资源不再被任何shared_ptr对象引用时释放资源和计数。 除了上述操作函数外,我们还可以实现一些辅助函数,例如获取资源指针、获取资源计数和重载箭头操作符等。 总之,实现Boost库的shared_ptr功能是一项复杂而繁琐的任务,在C语言中需要使用结构体和函数指针来模拟C++中的类和成员函数。这只是一个简单的概述,实际实现过程可能会更加复杂和困难。 ### 回答3: 通过C语言实现boost库的share_ptr可以参考以下步骤: 1. 首先,我们需要定义一个结构体来表示share_ptr,包含两个成员变量,一个是指向被共享的数据的指针(例如int*),另一个是一个整数计数,用于记录有多少个共享指针指向该数据。 ```c typedef struct{ void* pdata; int count; } share_ptr; ``` 2. 接下来,我们需要编写一系列的函数来操作share_ptr。首先是构造函数,用于创建一个新的share_ptr实例。在构造函数中,我们先将计数初始化为1,然后将数据指针赋值给share_ptr的成员变量。 ```c share_ptr* share_ptr_create(void* pdata){ share_ptr* sp = (share_ptr*)malloc(sizeof(share_ptr)); sp->pdata = pdata; sp->count = 1; return sp; } ``` 3. 然后,我们需要编写一个增加引用计数的函数,用于创建指向相同数据的新的share_ptr。在该函数中,我们只需要将计数加一即可。 ```c void share_ptr_add_ref(share_ptr* sp){ sp->count++; } ``` 4. 接下来,我们需要编写一个减少引用计数的函数,用于销毁share_ptr。在该函数中,我们首先将计数减一,然后判断计数是否为0,如果为0,表示没有其他share_ptr指向该数据,我们可以安全地释放该数据指针。 ```c void share_ptr_release(share_ptr* sp){ sp->count--; if(sp->count == 0){ free(sp->pdata); free(sp); } } ``` 5. 最后,我们需要编写一个获取数据指针的函数,用于在需要时访问被共享的数据。 ```c void* share_ptr_get(share_ptr* sp){ return sp->pdata; } ``` 通过以上的步骤,我们就可以用C语言实现一个简单的share_ptr类似于boost库中的share_ptr。当然,boost库中的share_ptr还有更多的功能和特性,这只是一个简单的实现示例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值