智能指针

一、智能指针的引入

int Func(int num1, int num2)
{
	if (num2 == 0)
	{
		throw string("除0错误");
	}
	return num1 / num2;
}

void TestFunc(int num1, int num2)
{
	try
	{
		Func(num1, num2);
	}
	catch (const string& s)
	{
		cout << s << endl;
	}
}

int main()
{
	int* p = new int();
	TestFunc(10, 0); 
	delete p; 
	return 0;
}

在上面的代码中,调用函数TestFunc中有捕获异常,则程序直接跳到捕获异常的地方 delete p,不能正确完成delete指针,可能造成内存泄漏,引发异常安全的问题,那如何解决呢?

(1)异常的重新抛出

(2)采用智能指针:调用的时候自动进行资源申请,调用结束后自动释放

设计思想

(1)RAII

资源获得即初始化,定义一个模板类,在构造的时候申请资源,在析构的时候释放资源

(2)像指针一样使用

重载operator*,operator->

二、auto_ptr

1. 发展过程

(1)儿童时期的auto_ptr 

构造函数的时候申请资源,析构函数的时候释放资源,并且重载operator* operator->,但是当进行拷贝构造和赋值操作的时候进行的是浅拷贝,也就是多个智能指针管理的是同一块空间,在调用结束释放空间的时候也就会对这块空间进行多次释放

(2)少年时期的auto_ptr 

在进行拷贝构造和赋值操作的时候,将原来这块空间的管理权从原指针 ap1 转移到拷贝构造的新指针 ap2 上,在进行浅拷贝之后,将原指针ap1赋为空指针nullptr,但是进行拷贝构造完成后 ap1 = NULL ,当我们再想访问 ap1 的时候就会解引用空指针导致程序崩溃

(3)成年后的auto_ptr 

在类的成员变量中加一个 bool 类型的 m_owner,用来判断该对象是否具有对这块空间的管理权,所以在进行拷贝构造和赋值运算符重载的时候,在浅拷贝之后,将原指针ap1的 m_owner 赋给现在的指针ap2 的 m_owner, 之后对原指针ap1的 m_owner赋为false,这样对这块空间的管理权就从ap1手中转移到了ap2手中,虽然 auto_ptr 思考了很多解决方案,但是它本身还是存在很大缺陷的,所以建议不要使用auto_ptr

template <class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr = nullptr)
		: m_ptr(ptr)
		, m_owner(false)
	{
		if (m_ptr)
			m_owner = true;
	}

	AutoPtr(const AutoPtr<T>& ap)
		: m_ptr(ap.m_ptr)
		, m_owner(ap.m_owner)
	{
		ap.m_owner = false;
	}

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

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

	AutoPtr<T>& operator=(const AutoPtr<T>& ap)
	{
		if (this != &ap)
		{
			if (m_ptr && m_owner)
			{
				delete m_ptr;
			}

			m_ptr = ap.m_ptr;
			m_owner = ap.m_owner;
			ap.m_owner = false;
		}
		return *this;
	}

	~AutoPtr()
	{
		if (m_ptr && m_owner)
		{
			delete m_ptr;
			m_ptr = nullptr;
			m_owner = false;
		}
	}

private:
	T* m_ptr;
	mutable bool m_owner;
};

三、unique_ptr

unique_ptr 也要有RAII和像指针一样的特点,它采用防拷贝的方式

template <class T>
class UniquePtr
{
public:
	UniquePtr(T* ptr = nullptr)
		: m_ptr(ptr)
	{}

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

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

	~UniquePtr()
	{
		if (m_ptr)
		{
			delete m_ptr;
			m_ptr = nullptr;
		}
	}

private:
	// C++98
	// UniquePtr(const UniquePtr<T>& up);
	// UniquePtr<T>& operator=(const UniquePtr<T>&);
	// C++11
	UniquePtr(const UniquePtr<T>& up) = delete;
	UniquePtr<T>& operator=(const UniquePtr<T>&) = delete;
private:
	T* m_ptr;
};

四、shared_ptr

在拷贝构造和赋值操作的时候,进行的是浅拷贝,在析构的时候会对同一块空间析构多次,对于这个问题,可以在拷贝构造和赋值运算符重载的时候,原指针 ap1 和 ap2 共享一个引用计数,对该引用计数进行++,在析构的时候,查看引用计数是否为1,若为1,表示只有当前指针使用这块空间,可以对该指针进行释放清理,若大于1,表示还有其它指针在使用这块空间,只需要将引用计数进行 - -,不需要释放这块空间

template<class T>
class SharedPtr
{
public:
	SharedPtr(T* ptr = nullptr)
		: m_ptr(ptr)
		, m_count(nullptr)
	{
		if (m_ptr)
		{
			m_count = new int(1);
		}
	}

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

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

	SharedPtr(const SharedPtr<T>& sp)
		: m_ptr(sp.m_ptr)
		, m_count(sp.m_count)
	{
		if (m_count)
			++(*m_count);
	}

	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (this != &sp)
		{
			Release();
			m_ptr = sp.m_ptr;
			m_count = sp.m_count;
			if (m_count)
				++(*m_count);
		}
	}

	~SharedPtr()
	{
		Release();
	}
private:
	void Release()
	{
		if (m_ptr && --(*m_couunt))
		{
			delete m_ptr;
			m_ptr = nullptr;
			delete m_count;
			m_count = nullptr;
		}
	}
private:
	T* m_ptr;
	int* m_count;
};

五、weak_ptr

但是shared_ptr会出现一个问题,就是循环引用,就会导致引用计数一直无法减到0,就会导致空间没有释放。例如如下代码

template<class T>
struct ListNode
{
      T _data;
      SharedPtr<ListNode<T>> _next;
      SharedPtr<ListNode<T>> _prev;

      ListNode(T data)
           :_data(data)
           , _next(NULL)
           , _prev(NULL)
      {}
};

void TestSharedPtr()
{
      SharedPtr<ListNode<int>> sp1(new ListNode<int> (10));
      SharedPtr<ListNode<int>> sp2(new ListNode<int>(20));
      sp1->_next = sp2;
      sp2->_prev = sp1;
}

shared_ptr采用weak_ptr解决循环引用的问题,将会出现循环引用的shared_ptr通过weak_ptr的构造函数保存下来,并且weak_ptr里面不增加引用计数,但是它不是严格意义上的智能指针,它是辅助shared_ptr的使用,为了解决shared_ptr循环引用的问题

template<class T>
class WeakPtr
{
public:
      WeakPtr(SharedPtr<T> sp)
           :m_ptr(sp.ptr)
      {}

      WeakPtr(const WeakPtr<T>& sp)
           :ptr(sp.m_ptr)
      {}

      WeakPtr<T>& operator=(const WeakPtr<T>& wp)
      {
           if (this != &wp)
           {
                 m_ptr = wp.m_ptr;
           }
           return *this;
      }

protected:
      T* m_ptr;
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值