有的时候使用malloc或者new创建的对象忘记释放就会导致内存泄漏,又或者此时释放语句之前有一段代码是抛异常的话,那么执行流就会乱跳,导致内存也无法释放。
比如这一段代码,at越界访问会导致抛异常,导致执行流跳出从而没有释放指针p。
void Func()
{
int* p = new int;
vector<int> v;
v.at(0) = 10;//会抛异常
delete p;//导致p没有释放
}
因此引入了智能指针来防止这种问题导致的内存泄漏。
RAII思想
智能指针的思想就是RAII,就是利用对象的生命周期来管控资源的一种方式。
在对象构造时获取资源,把资源交给对象管理,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。实际上是把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
- 不需要显式地释放资源。
- 采用这种方式,对象所需的资源在其生命期内始终保持有效。
智能指针的思想RAII(交给对象管理)
template<class T>
class SmartPtr
{
public:
SmartPtr(T* ptr)
:_ptr(ptr)
{
}
~SmartPtr()
{
cout <<"delete:"<< _ptr << endl;
delete _ptr;
}
private:
T* _ptr;
};
在对象的构造阶段把资源交给对象管理,对象的生命周期结束时会自动调用析构函数完成指针的释放。如图:抛出的异常被捕获,同时指针也释放了。
智能指针思想的变形(智能锁)
RAII思想可以扩展到锁上面,因为有时候加锁之后未解锁抛异常会导致执行流乱跳,而导致锁未释放,因此我们也可以基于此写一个智能锁,把这个锁交给一个对象管理,对象出了作用域就会自动调用析构函数完成锁的释放。但是这里需要注意的是,定义成员变量的时候要定义为引用,这样才能保证加锁的时候是加在同一个锁上的。
template<class T>//定义成模板就是如果有另外类型的锁来了也可以用这个SmartLock
class SmartLock
{
public:
SmartLock(T& lock)
:_lock(lock)
{
_lock.lock();
}
~SmartLock()
{
_lock.unlock();
}
private:
//使用引用就是为了保证加锁能加在同一个锁上面
T& _lock;//引用定义变量就是在定义它的时候初始化它
};
使用如下:
smtlock管理了这个mtx,出了作用域会自动调smtlock的析构函数完成锁的释放。
void add(int n, int *value)
{
SmartLock<mutex> smtlock(mtx);//用smtlock对象管理mtx这个锁,出了作用域会自动解锁
for (int i = 0; i < n; i++)
{
++(*value);
}
}
智能指针的两个要素
- RAII思想:把资源交给这个对象管理
- 能够像指针一样的行为(重载
*
和->
)
template<class T>
class SmartPtr
{
public:
//交给对象管理
SmartPtr(T* ptr)
:_ptr(ptr)
{
}
~SmartPtr()
{
delete _ptr;
}
//像指针一样的行为
T& operator*()//对象出了作用域还在,所以返回引用
{
return *_ptr;//返回这个对象
}
T* operator->()
{
return _ptr;//返回原生指针
}
private:
T* _ptr;
};
三种智能指针
1. auto_ptr(C++98)
C++98版本的库中提供了auto_ptr的智能指针。
想要实现一个智能指针就要实现这几个功能:RAII思想,像指针一样的行为。但是对象会有拷贝构造和赋值,auto_ptr的原理是进行了管理权的转移(管理权的转移就是,当把我的值给你之后,就把我置成空),这是一种带有缺陷的智能指针,会导致对象悬空。
实现的原理如下:
template <class T>
class auto_ptr
{
public:
auto_ptr(T* ptr)//构造
:_ptr(ptr)
{
<