C++----智能指针

因为C++中没有自主的内存回收机制,new出来的内存一定要使用dalete释放,否则就会造成内存泄露,因此有了智能指针,智能指针的原理就是收到开辟,系统释放

智能指针共分为4种,即boost库中的auto_ptr、scoped_ptr、shared_ptr、weak_ptr。

一、c++11 auto_ptr-----所有权唯一,即就是只能有一个智能指针指向该内存块,如果有新的智能指针指向此内存,则要将旧的智能指针置为NULL

代码如下:

template<typename T>
class SmartPtr
{
public:
	SmartPtr(T* ptr) :mptr(ptr){}
	~SmartPtr()
	{
		delete mptr;
		mptr = NULL;
	}
	SmartPtr(const SmartPtr<T>& rhs)
	{
		mptr = rhs.mptr;
		rhs.Release();
	}
	SmartPtr<T>& operator=(const SmartPtr<T>& rhs)
	{
		if (this != &rhs)
		{
			delete mptr;
			mptr = rhs.mptr;
			rhs.Release();
		}
		return *this;
	}
	T& operator*()
	{
		return *mptr;
	}
	T* operator->()
	{
		return mptr;
	}
private:
	void Release()const // const SmartPtr<T>* const this
	{
		(T*)mptr = NULL;
	}
	T* mptr;
};


int main()
{
	SmartPtr<int> sp1 = new int;
	SmartPtr<int> sp2 = sp1;//sp1.mptr = NULL;

**(1)***sp1 = 10;//出现问题

(2) int a = 10;
int
p = &a;
int
q = p;
p = 20;
return 0;
*
}

#endif

我们知道,普通的指针可以如蓝色部分一样使用,但是在auto_ptr中,我们可以看到绿色部分(1)会出现错误,因为在执行完前两句后,sp1.mptr == NULL,再执行 sp1 =10,程序就会崩溃,因为访问了保留区的数据。

auto_ptr存在以下几个缺陷:
1>不要使用auto_ptr保存一个非动态开辟空间的指针,因为在作用域结束的时候,会执行智能指针的析构函数,释放这块空间,但非动态的空间又无法释放;
2>不要使用两个auto_ptr指针指向同一个指针,具体原因上面解释过;
3>不要使用auto_ptr指向一个指针数组,因为auto_ptr的析构函数所用的是delete而不是delete[],不匹配;
4>不要将auto_ptr储存在容器中,因为赋值和拷贝构造后原指针无法使用

二、scoped_ptr----所有权不唯一,释放权唯一

template<typename T>
class SmartPtr
{
public:
	SmartPtr(T* ptr) :mptr(ptr), flag(true){}
	SmartPtr(const SmartPtr<T>& rhs)
	{
		mptr = rhs.mptr;
		flag = rhs.flag;
		rhs.flag = false;
	}
	SmartPtr<T>& operator=(const SmartPtr<T>& rhs)
	{
		if (this != &rhs)
		{
			this->~SmartPtr();
			mptr = rhs.mptr;
			flag = rhs.flag;
			rhs.flag = false;
		}
		return *this;
	}
	~SmartPtr()
	{
		if (flag)
		{
			delete mptr;
		}
		mptr = NULL;
	}
	T& operator*()
	{
		return *mptr;
	}
	T* operator->()
	{
		return mptr;
	}
private:
	T* mptr;
	mutable bool flag; //是否拥有释放权
};

// 智能指针提前销毁,其他智能指针失效
//SmartPtr<int> sp = sp1;    //mptr:0x100   flag:true
void Test(SmartPtr<int> sp)
{}
int main()
{
	SmartPtr<int> sp1(new int);//mptr:0x100   flag:false
	Test(sp1);
	*sp1 = 20;//
	//SmartPtr<int> sp1(new int);
	//SmartPtr<int> sp2(sp1);
	//*sp1 = 20;
	return 0;
}

可以看到scoped_ptr和auto_ptr一样,都有因为权限转移而引起的问题,为了防止权限转移,应将拷贝构造和赋值运算符的重载函数都放在私有成员下,表明了scoped_ptr无法用对象来生成另一个对象或给另一个对象赋值,提高了安全性,但是又存了在无法“++”、“–-”这些操作,所以这种形式叶并不是最完美的。所以又有了shared_ptr。

三、shared_ptr----带有引用计数的智能指针

下面是代码:

class Ref_Management
{
public:
	static Ref_Management* getInstance()
	{
		return &rm;
	}
private:
	Ref_Management():cursize(0){}
	Ref_Management(const Ref_Management&);
	static Ref_Management rm;
public:
	void addref(void* mptr)
	{
		if (mptr != NULL)
		{
			int index = find(mptr);
			if (index < 0)
			{
				Node tmp(mptr, 1);
				node[cursize++] = tmp;
				//node[cursize].addr = mptr;
				//node[cursize].ref = 1;
				//cursize++;
			}
			else
			{
				node[index].ref++;
			}

		//std::vector<Node>::iterator fit = find(mptr);
		//if (fit == vec.end())
		//{
		//	Node node(mptr, 1);
		//	vec.push_back(node);
		//}
		//else
		//{
		//	(*fit).ref++;
		//}
	}
}
void delref(void* mptr)
{
	if (mptr != NULL)
	{
		int index = find(mptr);
		if (index < 0)
		{
			throw std::exception("addr is not exsit!");
		}
		else
		{
			if (node[index].ref != 0)
			{
				node[index].ref--;
			}
		}
	}
}
int getref(void* mptr)
{    
	int rt = -1;
	if (mptr != NULL)
	{
		int index = find(mptr);
		if (index >= 0)
		{
			rt = node[index].ref;
		}
	}
	return rt;
}
private:
	int find(void* mptr)
	{
		int rt = -1;
		for (int i = 0; i < cursize; i++)
		{
			if (node[i].addr == mptr)
			{
				rt = i;
				break;
			}
		}
		return rt;
		/*std::vector<Node>::iterator it = vec.begin();
		for (it; it != vec.end(); it++)
		{
			if ((*it).addr == mptr)
				break;
		}
		return it;*/
	}
	class Node
	{
	public:
		Node(void* padd = NULL, int rf = 0) :addr(padd), ref(rf){}
	public:
		void* addr;
		int ref;
	};
	Node node[10];
	int cursize;//
};
Ref_Management Ref_Management::rm;
    template<typename T>
    class Shared_Ptr
    {
    public:
    	Shared_Ptr(T* ptr = NULL) :mptr(ptr)
    	{
    		AddRef();
    	}
    	Shared_Ptr(const Shared_Ptr<T>& rhs) :mptr(rhs.mptr)
    	{
    		AddRef();
    	}
    	Shared_Ptr<T>& operator=(const Shared_Ptr<T>& rhs)
    	{
    		if (this != &rhs)
    		{
    			this->~Shared_Ptr();
    			mptr = rhs.mptr;
    			AddRef();
    		}
    		return *this;
    	}
    	~Shared_Ptr()
    	{
    		DelRef();
    		if (GetRef() == 0)
    		{
    			delete mptr;
    		}
    		mptr = NULL;
    	}
    	T* operator->()
    	{
    		return mptr;
    	}
    	T& operator*()
    	{
    		return *mptr;
    	}
    private:
    	void AddRef()
    	{
    		prm->addref(mptr);
    	}
    	void DelRef()
    	{
    		prm->delref(mptr);
    	}
    	int GetRef()
    	{
    		return prm->getref(mptr);
    	}
    	T* mptr;
    	static Ref_Management* prm;
    };
    template<typename T>
    Ref_Management* Shared_Ptr<T>::prm = Ref_Management::getInstance();

shared_ptr和以上两者的区别就是加入了引用计数,用于检测当前对象所管理的指针是否还被其他shared_ptr对象的指针使用,在析构函数时对其引用计数减一,判断是否为0,若为0,则释放这个指针和这个引用计数的空间。这个原理和String类的写时拷贝原理是一样的。

然而,智能指针也存在问题,下面就来看一下

class B;
class A
{
public:
	A()
	{
		std::cout << "A()" << std::endl;
	}
	~A()
	{
		std::cout << "~A()" << std::endl;
	}
public:
	Shared_Ptr<B> spa;
};
class B
{
public:
	B()
	{
		std::cout << "B()" << std::endl;
	}
	~B()
	{
		std::cout << "~B()" << std::endl;
	}
public:
	Shared_Ptr<A> spb;
};

int main()
{
	Shared_Ptr<A> pa(new A());
	Shared_Ptr<B> pb(new B());
	pa->spa = pb;//存在问题
	pb->spb = pa;//存在问题
	return 0;
}

运行上面代码我们可以发现,只有A和B的构造,而没有析构,是什么原因造成了内存泄露

在这里插入图片描述
在两个shared_ptr指针相互指向时,引用计数变为2,因此在释放时调用析构引用计数会变为0,并不会被真正释放,因此有了weak_ptr。

template<typename T>
class Weak_ptr
{
public:
	Weak_ptr(T* ptr = NULL) :mptr(ptr){}
	Weak_ptr(const Weak_ptr<T>& rhs)
	{
		mptr = rhs.mptr;
	}
	Weak_ptr<T>& operator=(const Weak_ptr<T>& rhs)
	{
		if (this != &rhs)
		{
			mptr = rhs.mptr;
		}
		return *this;
	}
	Weak_ptr<T>& operator=(const Shared_Ptr<T>& rhs)
	{
		mptr = rhs.getPtr();
		return *this;
	}
	~Weak_ptr()
	{
	}
	T* operator->()
	{
		return mptr;
	}
	T& operator*()
	{
		return *mptr;
	}
private:
	T* mptr;
};

weak_ptr是解决两个shared_ptr相互指向的问题
1、不能单独使用,要和shared_ptr配合使用
2、不加引用计数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值