c++中智能指针(一)

17 篇文章 1 订阅

一、智能指针的引入:

智能指针为什么要引入呢?看下边的代码:

void fun()
{
       int *p = new int(2);
       if(1)
             throw 1;
       delete p;
}
int main()
{
        try
       {
              fun();
        }
       catch(...)
      {
              cout<<"未知异常"<<endl;
       }
       return 0;
}



很明显,上边的代码因为抛出异常而改变的代码的执行流,导致delete无法执行,最终

内存泄漏,智能指针就是来解决这个问题的。所谓智能指针就是自动的管理指针所指向

的动态资源的释放工作。智能指针其实并不是指针,它一个类,但是它却做着和指针类似的事儿(管理资源):在构造函数中完成资源的分配和初始化,在析构函数中完成资源的清理和回收,保证资源的正确初始化和释放.这也就提出了一个和智能指针类似的概念-RAII(Resource Acquisition Initialization),资源分配及初始化,定义一个类来封装资源的分配和释放

二、智能指针的分类:

1、auto_ptr:通过权限转移的方式来防止多个指针同时指向同一块内存空间造成释放时

同一块空间释放多次的情况。权限转移主要体现在:copy构造时,用已有对象创建新的

对象时,必须令原有的对象指向空。赋值运算符重载时,用一个已经存在的对象1给另

个已经存在的对象2赋值,这时对象1的指针必须置为NULL。

【模拟实现】

template <typename T>
class AutoPtr
{
public:
	AutoPtr(T* ptr)
		:_ptr(ptr)
	{}
	~AutoPtr()
	{
		if (_ptr != NULL)
		{
			delete _ptr;
		}
	}
	AutoPtr(AutoPtr<T>& ap)
	{
		_ptr = new T;
		_ptr = ap._ptr;
		ap._ptr = NULL;
	}
	AutoPtr<T>& operator=(AutoPtr<T>& ap)
	{
		if (this != &ap)
		{
			delete _ptr;
			_ptr = ap._ptr;
			ap._ptr = NULL;
			return *this;
		}
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	void Display()
	{
		if (_ptr == NULL)
		{
			cout << "指针为空" << endl;
		}
		else
		{
			cout << *_ptr << endl;
		}
	}
private:
	T*  _ptr;
};


当然也是有人认为不需要权限转移也可以实现上述智能指针:在智能指针的成员变量中

再加一个成员owner,比如用对象1拷贝构造对象2时,对象2的owner变为true,对象1的

owner变为false,其实这样是会存在安全问题,还是两个指针指向同一块内存。如果再

稍加设计:析构对象时,如果所对的owner为false,就不予析构;get函数(返回对象

的_ptr)中,如果该对象的owner是false,返回NULL,否则返回_ptr.这样子的实现其实

也可以,就是稍微麻烦。

2、scoped_ptr:存在于boost库中。通过防拷贝、防赋值来防止多个指针指向同一块内

存。那么怎样实现防拷贝、防赋值呢?把拷贝构造函数和赋值运算符重载声明为private

或者protected即可,不需实现。切记不要声明为public,因为你永远不知道谁为为你实

现这个代码。

【模拟实现】

template<typename T>
class ScopedPtr
{
public:
	ScopedPtr(T* ptr)
		:_ptr(ptr)
	{}
	~ScopedPtr()
	{
		if (_ptr != NULL)
		{
			delete _ptr;
		}
	}
	T& operator*() const
	{
		if (_ptr != NULL)
		{
			return *_ptr;
		}
	}
	T* operator->() const
	{
		return _ptr;
	}
	void Display()const
	{
		if (_ptr != NULL)
		{
			cout << *_ptr << endl;
		}
	}
private:
	ScopedPtr(const ScopedPtr<T>& sp);
	ScopedPtr<T>& operator=(const ScopedPtr<T>& sp)
	{}
	T* _ptr;
};


细心的你或许会发现,我在赋值运算符重载时加了一对花括号。我的IDE是vs2015,要是仅仅声明就会报错,所以我这样做的。

3、shared_ptr:同样存在于boost库。通过引用计数的方法来防止同一块内存被释放多

次,这个就有点类似于写时拷贝来string类。这也是我们最常使用的智能指针。

【模拟实现】

template<typename T>
class SharedPtr
{
public:
	SharedPtr(T* ptr)
		:_ptr(ptr)
		,_pCount(new int (1))
	{}
	~SharedPtr()
	{
		if (--*_pCount == 0)
		{
			delete _ptr;
			delete _pCount;
		}
	}
	SharedPtr(const SharedPtr<T>& sp)
	{
		_ptr = sp._ptr;
		_pCount = sp._pCount;
		++*_pCount;
	}
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			if (_ptr != nullptr)
			{
				if (--*_pCount == 0)
				{
					delete _ptr;
					delete _pCount;
				}
			}
			else
			{
				_ptr = sp._ptr;
				_pCount = sp._pCount;
				++(*_pCount);
			}
		}
		return *this;
	}
private:
	T* _ptr;
	int* _pCount;
};


特别注意:赋值运算符重载中的多种情况,当然也可以使用现代的方法(即就是swap

的方法)完成。

上边多次提到boost库,那么我们如何将boost库装到我们的IDE下呢?下边提供一个简

单的办法:

官网下载boost--->解压----->放到编译器的主目录的VC的include目录下。

貌似这种方法并不是适合所有的。如果这种方法不适合你的机器,你可以去网上找别的

教程完成安装。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值