智能指针的分析与实现

一,为什么要用智能指针

在编写c++程序的时候,让我们最头痛的问题就是内存泄露,也就是说

int* pt = new int;

delete pt;

必须保证new和delete必须成对出现。作为程序猿,可以像使用普通变量一样来使用指针,这个指针可以在恰当的时候被自动释放,智能指针就是这样一个指针,它的任务是保证每一个被动态分配的内存都能够被释放。

看一个例子


如果在DealProcessAdoption有一个异常,会发生什么事情。

所以这段代码很危险,当DealProcessAdoption有一个exception,后面的delete代码就会跳过,从而造成内存泄露。

有两种解决方案

一种是用try catch,另外一种就是用智能指针。


二,一个智能指针的实际例子

class intptr 

private: 
 int* m_p; 
public: 
 intptr(int* p){ m_p = p; } 
 ~intptr(){ delete m_p; } 
 int& operator*(){ return *m_p; } 
};

我们可以方便的执行以下代码,而不必担心内存泄漏的问题:
somefunction()
{
 intptr pi(new int); 
 *pi = 10; 
 int a = *pi; 
}

以上我们给出的智能指针有个致命错误。设想我们执行以下代码会有怎样的情况发生:
void somefunction()
{
 intptr pt1(new int);
 intptr pt2(new int);
 *pt1 = 10;
 pt2 = pt1;
}

对于普通指针来说,pt2 = pt1只是让pt2指向与pt1相同的地址,但是对于我们的智能指针来说,pt2原先指向的地址被泄露掉了,而pt1所指向的地址被释放了两次。所以,我们给每个new出来的内存地址对应的分配一个“被指向计数器”,由它记录这块内存地址被多少指针所指向。

三,boost库中的智能指针

 但是这个auto_ptr有很多缺点

1、auto_ptr不能共享所有权。
2、auto_ptr不能指向数组
3、auto_ptr不能作为容器的成员。
4、不能通过赋值操作来初始化auto_ptr
std::auto_ptr<int> p(newint(42));     //OK
std::auto_ptr<int> p = newint(42);    //ERROR
这是因为auto_ptr 的构造函数被定义为了explicit,不能隐式调用
5、不要把auto_ptr放入容器

boost库的share_ptr


shared_ptr是Boost库所提供的一个智能指针的实现,shared_ptr就是为了解决auto_ptr在对象所有权上的局限性(auto_ptr是独占的),在使用引用计数的机制上提供了可以共享所有权的智能指针.
2. shared_ptr比auto_ptr更安全
3. shared_ptr是可以拷贝和赋值的,拷贝行为也是等价的,并且可以被比较,这意味这它可被放入标准库的一般容器(vector,list)和关联容器中(map)。

四,智能指针的两种实现方式

方案一(引入辅助类)


方案二(句柄类)

测试:


智能指针的另外一种标准实现

namespace smart
{
	// 引用计数类.
	class smart_count
	{
	public:
		smart_count(int c = 0) : use_count(c) {}
		~smart_count() {}

		// 增加引用计数, 并返回计数值.
		int addref() { return ++use_count; }
		// 减少引用计数, 并返回计数值.
		int release() { return --use_count; }
	private:
		// 计数变量.
		int use_count;
	};
	// 智能指针.
	template <class T>
	class smart_ptr
	{
	public:
		// 构造指针, 并使引用计数置为1.用explicit是防止隐式转换
		explicit smart_ptr (T* ptr) : p(ptr), u(new smart_count(1))
		{}
		// 构造空指针.
		explicit smart_ptr () : p(NULL), u(NULL)
		{}
		// 智能指针析构.
		~smart_ptr (void)
		{
			// 如果引用计数等于0, 则删除数据和引用计数, 并置p为NULL.
			// 此处需要注意的是, 共用的u并未置为 NULL, 在其它指针析构
			// 时, p为NULL, 则不会重复delete.
			if (p && u->release() <= 0)
			{
				delete p;
				delete u;
				p = NULL;
			}  
		}
		// 智能指针拷贝构造函数.
		smart_ptr (const smart_ptr<T>& t)
		{
			p = t.p;
			u = t.u;

			if (u) // 必须判断空值.
			{
				u->addref(); // 增加引用计数.
			}
		}
		// 指针赋值.
		void operator= (smart_ptr<T>& t)
		{
			// 首先将引用计数减1, 然后再判断是否小于0, 如果小于0, 则delete.   
			if (p && u->release() <= 0)
			{
				delete p;
				delete u;
			}

			// 直接赋值.
			p = t.p;
			u = t.u;

			if (u) // 必须判断空值.
			{
				u->addref(); // 增加引用计数.
			}
		}
		// 重载->操作和*操作符.
		T *operator-> (void) { return p; }
		T& operator *(void) { return *p; }
		// 重载!操作符.
		bool operator! () const { return !p;}

		// 重载指针bool值操作符.
		typedef smart_ptr<T> this_type;
		typedef T * this_type::*unspecified_bool_type;
		operator unspecified_bool_type() const { return !p ? 0: &this_type::p; }
		// 得到原指针.
		T* get() { return p; }
		void reset(T* ptr)
		{
			// 首先将引用计数减1, 然后再判断是否小于0, 如果小于0, 则delete.   
			if (p && u->release() <= 0)
			{
				delete p;
				delete u;
			}
			// 赋值, 如果是NULL, 则不创建引用计数.
			p = ptr;
			if (p)
				u = new smart_count(1);
			else
				u = NULL;
		}
		void reset(smart_ptr<T>& t)
		{
			// 首先将引用计数减1, 然后再判断是否小于0, 如果小于0, 则delete.   
			if (p && u->release() <= 0)
			{
				delete p;
				delete u;
			}

			// 赋值.
			p = t.p;
			u = t.u;

			if (u) // 必须判断空值.
			{
				u->addref(); // 增加引用计数.
			}
		}
	private:
		T* p;
		smart_count* u;
	};
	// 重载==操作符.
	template<class T, class U> inline bool operator==(smart_ptr<T> & a, smart_ptr<U> & b)
	{
		return a.get() == b.get();
	}
	// 重载!=操作符.
	template<class T, class U> inline bool operator!=(smart_ptr<T> & a, smart_ptr<U> & b)
	{
		return a.get() != b.get();
	}
}



猜你喜欢

转载自blog.csdn.net/zhangjingyangguang/article/details/27334053
今日推荐