智能指针

折月煮酒 提交于 2019-11-26 17:52:32

针对C++没有实现自己的内存回收机制,所以它引入了智能指针进行内存回收,力求做到内存由用户开辟,系统回收这样就能最大程度避免内存泄漏的发生。具体做法是以智能指针(类)生成一个对象(栈上生成)对开辟的内存(堆上)进行自主回收管理。

  • C ++ 98 中引入了auto_ptr智能指针,其宗旨是维护内存所有权的唯一性,具体的做法就是对一个已经开辟内存块只能有一个智能指针指向,如果有新的智能指针对象指向该内存就讲就得智能指针指向断开。
template<typename T>
class Auto_Ptr
{
public:
	Auto_Ptr(T* ptr) :mptr(ptr){}

	~Auto_Ptr()
	{
		delete mptr;
		mptr = NULL;
	}

	Auto_Ptr(Auto_Ptr<T>& rhs)    //所有权更替
	{
		mptr = rhs.Release();
	}

	Auto_Ptr<T>& operator=(Auto_Ptr<T>& rhs)     //所有权更替
	{
		if (this != &rhs)
		{
			delete mptr;
			mptr = rhs.Release();       
		}
		return *this;
	}

	T& operator*()
	{
		return *mptr;
	}

	T* operator->()
	{
		return mptr;
	}

private:
	T* Release()      //保存旧的智能指针的指向,释放所有权
	{
		T* ptr = mptr;
		mptr = NULL;
		return ptr;
	}
	T* mptr;
};

int main()
{
	std::auto_ptr<int> ap1(new int);
	std::auto_ptr<int> ap2 = ap1;

	*ap1 = 10;

	return 0;
}
  • C++11又引入了三个新的智能指针unique_ptr、shared_ptr 、weak_ptr,当然新的东西的引入肯定是因为旧的有缺陷。
  • unique_ptr:auto_ptr确实是从设计层面上保证了内存所有权的唯一性,但是拷贝构造和赋值运算符重载都会造成智能指针提前失效(所有权更替),所以unique_ptr再次在设计层面上扼杀了它的拷贝构造、赋值运算符重在操作(具体措施就是将它的声明写在智能指针类的私有下,因为没必要做具体实现)。
template<typename T>
class Unique_Ptr
{
public:
	Unique_Ptr(T* ptr) :mptr(ptr){}

	~Unique_Ptr()
	{
		delete mptr;
		mptr = NULL;
	}

	T& operator*()
	{
		return *mptr;
	}

	T* operator->()
	{
		return mptr;
	}
private:
	Unique_Ptr(Unique_Ptr<T>&);

	Unique_Ptr<T>& operator =(Unique_Ptr<T>&);

	T* mptr;
};

int main()
{
	int* p = new int;
	Unique_Ptr<int> up1(p);
	Unique_Ptr<int> up2(p);
	Unique_Ptr<int> up3(p);
	return 0;
}

不难看出Unique_Ptr确实解决了指针提前失效的问题,但是指针还是不能共享内存上的数据
  • shared_ptr:强智能指针,解决数据共享问题;就单个内存块而言我们应该允许它被多个智能指针指向,具体多少个指针指向必须记录下来,因为释放内存的时机显然是没有指针指向该内存是释放,释放自然要获取内存块的地址,也就是一个指针。一个引用计数,一个指针足以表示单个内存块实时利用信息-----------------设计一个结构体;
  1. 设计思路:对于shared_ptr来说为它提供一个管理的接口,它对相应内存块对应引用计数的管理也就是对内存的管理,无非是++引用计数、--引用计数、获取引用计数;因此将引用计数操作封装成一个类,并且我们希望对不同类型实例化的智能指针进行统一管理,所以开辟一块内存用于存储它们的信息(地址、引用次数)。
	typedef struct ref
	{
		void* addr;
		int count;
	}Ref;

	Ref ref_count[10];
  1. 图示:

  1. 代码:
class Ref_Manage
{
public:
	static Ref_Manage* getInstance()  //引用计数类管理接口,写成静态方法,所有对象共有
	{
		if (prm == NULL)
		{
			prm = new Ref_Manage();
		}
		return prm;
    }

	void addRef(void* ptr)
	{
		if (ptr == NULL)
		{
			return;
		}
		int index = find(ptr);
		if (index != -1)
		{
			ref_count[index].count++;
		}
		else
		{
			ref_count[cursize].addr = ptr;
			ref_count[cursize++].count = 1;
		}
	}

	void delRef(void* ptr)
	{
		if (ptr == NULL)
		{
			return;
		}
		int index = find(ptr);
		if (index != -1)
		{
			if (getRef(ptr) != 0)
			{
				ref_count[index].count--;
			}
		}
	}

	int getRef(void* ptr)
	{
		if (ptr == NULL)
		{
			return -1;
		}

		int index = find(ptr);
		if (index == -1)
		{
			throw std::exception("ptr is error!");
		}
		return ref_count[index].count;
	}

private:

	Ref_Manage()
	{
		cursize = 0;
	}

	Ref_Manage(const Ref_Manage&);

	int find(void* ptr)//下标
	{
		int index = -1;
		for (int i = 0; i < cursize; i++)
		{
			if (ref_count[i].addr == ptr)
			{
				index = i;
				break;
			}
		}
		return index;
	}

	typedef struct ref
	{
		void* addr;
		int count;
	}Ref;

	Ref ref_count[10];

	int cursize;

	static Ref_Manage* prm;
};

Ref_Manage* Ref_Manage::prm = NULL;

template<typename T>

class Shared_Ptr
{
public:
	Shared_Ptr(T* ptr = NULL)
	{
		mptr = ptr;
		rm->addRef(mptr);
	}

	Shared_Ptr(Shared_Ptr<T>& rhs)
	{
		mptr = rhs.mptr;
		rm->addRef(mptr);
	}

	Shared_Ptr<T>& operator=(Shared_Ptr<T>& rhs)
	{
		if (this != &rhs)
		{
			rm->delRef(mptr);
			if (rm->getRef(mptr) == 0)
			{
				delete mptr;
			}
			mptr = rhs.mptr;
			rm->addRef(mptr);
		}
		return *this;
	}

	~Shared_Ptr()
	{
		rm->delRef(mptr);
		if (rm->getRef(mptr) == 0)
		{
			delete mptr;
		}
		mptr = NULL;
	}

	T& operator*()
	{
		return *mptr;
	}

	T* operator->()
	{
		return mptr;
	}
private:
	T* mptr;
	static Ref_Manage* rm;
};

template<typename T>
Ref_Manage* Shared_Ptr<T>::rm = Ref_Manage::getInstance(); 

int main()
{
    int* p = new int;
	Shared_Ptr<int> sp1(p);
	Shared_Ptr<int> sp2(p);
	Shared_Ptr<int> sp3(p);//实现了数据共享


	std::shared_ptr<A> pa(new A());//下面的操作会产生强智能指针互相引用的问题
	std::shared_ptr<B> pb(new B());
	pa->spa = pb;
	pb->spb = pa;
	return 0;
}
  • weak_ptr: 弱智能指针,解决强智能指针相互引用的问题------不加引用计数、不能单独使用,必须配合强智能指针使用(保证了在释放的时候不会因为引用计数不为0而没有正确释放,造成内存泄漏)

       引用计数有一个问题就是互相引用形成环,这样两个指针指向的内存都无法释放。需要手动打破循环引用或使用weak_ptr。         weak_ptr是一个弱引用,只引用,不计数。如果一块内存被shared_ptr和weak_ptr同时引用,当所有shared_ptr析构之后,         不管还有没有weak_ptr引用该内存,内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的,在使用之前需要检         查weak_ptr是否为空指针.

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!