今天我们来详解C++中一个重要的知识点——智能指针。文章主要以下面几点来展开:
- 智能指针定义及动态内存的管理
- 智能指针作用
- auto_ptr的模拟实现
智能指针定义
智能指针简单理解就是智能的指针,它可以智能/自动化的管理指针所指向的动态内存的释放。
智能指针和普通指针的区别在于智能指针实际上是对普通指针加了一层封装机制(利用了RAII技术),这样做的目的是为了使得智能指针可以方便的管理一个对象的生命周期。
智能指针实质上是一个对象,行为表现的却像一个指针。
我们简要了解一下RAII(Resource Acquisition Is Initialization)
资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。
boost库中的智能指针定义在namespace boost中,包括:
shared_ptr
weak_ptr
scoped_ptr
shared_arr
scoped_arr
有一个问题值得大家思考,为什么会出现智能指针?
动态内存的管理非常容易出错,使用new和delete管理动态内存存在三个常见问题:
1,忘记delete内存,忘记释放动态呢村会导致内存泄漏问题,因为这种内存永远不可能被归还给自由空间了。查找内存泄漏错误是非常困难的,因为通常应用程序运行很长时间后,真正内存耗完时,才能检测到这种错误。
2,使用已经释放掉的对象。通过在内存释放后将指针置为空,有时可以检测出这种错误。
3,同一块内存释放两次。当有两个指针指向相同的动态分配对象时,可能发生这种错误。如果对其中一个指针进行了delete操作,对象的内存就被归还给自由空间了。这就造成了另外一个指针成为野指针(悬垂指针)。如果随后又delete这个野指针,造成同一块内存被释放两次,自由空间就可能被破坏。
智能指针便很好地可以避免这些问题。对于一块内存,只有在没有任何智能指针指向它的情况下,智能指针才会自动的释放它。它可以使我们更方便跟安全地使用动态内存。这里我先插入点辅助知识,有助大家理解。
智能指针的作用
1,防止忘记调用delete,避免内存泄漏;
2,异常安全。在一段进行了try/catch的代码段里,即使你写了delete,也有可能因为发生异常,程序进入catch块,从而未释放内存,而智能指针可以解决。
3,更深一层含义,可以把value语义转化为reference语义。
r
智能指针的模拟实现
1,auto_ptr
auto_ptr是在std库中的,不在boost库中,也来解释一下。
auto_pt用于指向一个动态分配的对象指针,他的析构函数用于删除所指对象的空间,以此达到对对象生存期的控制。
auto_ptr本质是管理权的转移。在进行赋值,拷贝构造时,会对控制权进行转移。怎么理解呢?我们用图来解释其中的问题。(我自己理解的,大家可以提出其中问题)
拷贝构造
赋值
模拟实现auto_ptr
class AutoPtr
{
public:
AutoPtr(T* ptr = NULL)
:_ptr(ptr)
{}
AutoPtr(const AutoPtr<T>& a)
{
_ptr = a._ptr;
a._ptr = NULL;
}
AutoPtr<T>& operator=(const AutoPtr<T>& a)
{
if (this != &a)
{
delete this->_ptr;
this->_ptr = a._ptr;
a._ptr = NULL;
}
return *this;
}
~AutoPtr()
{
delete _ptr;
}
T& operator*()
{
if (_ptr == NULL)
{
throw a; //抛出异常对象
}
return *_ptr;
}
T* operator->()
{
if (_ptr == NULL)
{
throw a;
}
return _ptr;
}
bool operator==(const AutoPtr<T>& a)
{
return _ptr == a._ptr;
}
bool operator!=(const AutoPtr<T>& a)
{
return _ptr != a._ptr;
}
void Reset(T* ptr = NULL)
{
if (_ptr != ptr)
{
delete _ptr;
}
_ptr = ptr;
}
protected:
T* _ptr;
};
上面有几点需要说明:
1,在对“*”进行重载
如果返回值写成”T ”,而不是“T &”。若出现下面赋值语句:“*P1 = 12”,则编译不会通过。
2,在对“->”进行重载
看下面这句语句:”P1->A = 30” //P1.operator->()->A = 30;
即P1->->A;但是这个可读性太差,编译器进行了优化,P1->A;
好了今天就讲解这些,boost库中的其他智能指针在下一篇《智能指针(下)》会模拟实现并剖析他们的框架。