C++智能指针的简单实现

什么是智能指针

智能指针就是在c++当中将指针进行了封装,在保留了指针当中的各种各样的操作的同时,为指针增添了内存管理的功能

智能指针,普通指针

和普通指针相比,智能指针更加能够防范指针悬空的事情的发生.
在c++当中,是没有内存自动回收的管理机制的,那么,内存的释放与生成,全靠程序员的自觉,要是有一个内存块被new生成了出来,却没有delete掉,那么就会产生内存泄漏的风险

比如

int * p = new int(1);
int * p1 = p;
int * p2 = p1;

std::cout << *p << std::endl;
std::cout << *p1 << std::endl;
std::cout << *p2 << std::endl;

delete p;

std::cout << *p1 << std::endl;

在这种情况下,p1,p2都成了没有归处的野指针了.
出现这种情况的原因很简单,因为每一个指针都是独立的,他不知道这个内存块是不是还有其他指针指向,因此,就会出现了这种问题了

ps:

即使不是上述那种情况,仅仅是new了一个对象,没有进行传递,然后操作完成后进行delete也是有可能出现内存泄漏的风险的,那就是当程序中间抛出了异常的时候,delete语句就有可能被跳过

引用计数

在c++当中就提供了三种智能指针share_ptr,unquie_str,weak_ptr,每一种都有他的用途,当然,用的最多的还是share_ptr.而share_ptr使用的就是引用计数这种技术

而这种技术简单来说就是让指针知道这个内存块是否还有其他的指针引用,若有,那么将不会对这个内存块指向delete操作

引用计数是计算机编程语言中的一种内存管理技术,是指将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程。使用引用计数技术可以实现自动资源管理的目的。同时引用计数还可以指使用引用计数技术回收未使用资源的垃圾回收算法。

当创建一个对象的实例并在堆上申请内存时,对象的引用计数就为1,在其他对象中需要持有这个对象时,就需要把该对象的引用计数加1,需要释放一个对象时,就将该对象的引用计数减1,直至对象的引用计数为0,对象的内存会被立刻释放。
------维基百科

总的来说,引用计数有两个优点:

1.简化跟踪堆当中的对象的过程.一个对象被new了出来之后,对其进行跟踪是很重要的,因为对象的所有者要负责对对象的delete操作,但是对象的所有者可以有很多个,而且对象的所有权也是可以转移的,因此,对对象的追踪并不是一件简单的事情.而引用计数可以比较简单的记录这些东西

2.节省内存,提升效率 在为一个相同的值创造了很多份拷贝是一件非常浪费内存的事情,假若创建了对象且只使用一次,在删除该对象的同时,创建了一个新的相似的对象,那么可以将删除该对象的操作改为修改原对象的内存,从而提高效率,而引用计数可以为此提供参考

当然使用引用计数来作为垃圾回收机制的方法也会有一些缺点,比如循环引用问题,即

class A {
	std::share_ptr<B> pB;
};

class B{
	std::share_ptr<A> pA;
}

int main(){
	//此时a的计数为1
	std::share_ptr<A>  p1(new A);
	//此时b的计数为1
	std::share_ptr<B>  p2(new B);

	//此时b的计数为2
	p1.pB = p2;
	//此时a的计数为2
	p2.pA = p1;
	return 0;
}

这样,内存是无法被释放的,因为即使销毁了p1,p2引用计数也不会被归零的
当然在c++当中引入了weak_ptr来解决这个问题,不过,本文主要还是讨论如何实现引用计数的智能指针

实现

既然是引用计数的指针,那么所封装的内容自然就是指针本身和一个引用计数的标识符(int,size_t…反正就是记录被引用的次数)

除此之外,就是要对其中的拷贝操作,=操作,以及->,*,进行重载,以便于实现指针的原本的功能以及完成引用计数
主要思路在于,在进行拷贝=操作的时候都会对引用计数进行修改,而当这个类离开了自己的作用域的时候,也就是调用析构函数的时候,将会对引用计数进行减少,当被引用数为0的时候,则释放该指针的内存,从而实现了智能指针的效果

代码

template <typename T>
class SmartPtr
{
	//被封装的指针
	T * ptr;    
	//引用计数(若不使用指针那么,岂不是起不到计数的作用啦)
	int * count;
public:
	explicit SmartPtr<T>(T *ptr);
	SmartPtr(const SmartPtr &ptr);

	~SmartPtr();

	SmartPtr& operator= (const SmartPtr& p);
	T& operator*();
	T* operator->();

};

然后是实现


template <typename T>
SmartPtr<T>::SmartPtr(T* ptr):ptr(ptr),count(new int(1))
{
}

template <typename T>
SmartPtr<T>::SmartPtr(const SmartPtr& ptr)
{
	//若不是自身则进行拷贝操作
	if(this != &ptr)
	{
		this->ptr = ptr.ptr;
		this->count = ptr.count;
		++(*this->count);
	}
}

template <typename T>
SmartPtr<T>::~SmartPtr()
{
	//	减少计数
	--(*this->count);
	//引用数为0则释放内存
	if((*this->count) == 0)
	{
		delete ptr;
		delete count;
	}
}

template <typename T>
SmartPtr<T>& SmartPtr<T>::operator=(const SmartPtr& p)
{
	//若是同一个指针就没有必要操作了
	if(this->ptr == p.ptr)
	{
		return *this;
	}

	//若当前指针还存在,那么就是减少引用次数(为0则释放内存)
	if(this->ptr)
	{
		--(*this->count);
		if((*this->count) == 0)
		{
			delete this->ptr;
			delete this->count;
		}
	}
	
	//指向=的效果
	this->ptr = p.ptr;
	this->count = p.count;
	
	//增加引用计数
	++(*this->count);
	return *this;
}



template <typename T>
T& SmartPtr<T>::operator*()
{
	assert(this->ptr != nullptr);
	return *(this->ptr);
}

template <typename T>
T* SmartPtr<T>::operator->()
{
	assert(this->ptr != nullptr);
	return this->ptr;
}

至此一个简单的智能指针就实现了

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值