智能指针
基础概念
为什么要有智能指针
首先先看一段程序,看看这段程序可能会出现什么问题。
#include <iostream>
using namespace std;
void Func()
{
int* ptr = new int[10];
//...假设这其中有很多代码
throw "error!";
// delete[] ptr;
}
int main()
{
try
{
Func();
}
catch(const char* str)
{
cout << str;
}
}
首先这个代码中我们由于可能因为代码过于复杂而忘记释放空间,导致内存泄露,其次如果我们在这段代码中抛出异常,导致函数终止也会导致无法释空间,内存泄露,这种问题也被称为异常安全问题。作为Cpp程序猿,我们最烦也是最应该提防的一个问题就是内存泄露。那么针对这种情况我们象让我们动态分配的内存空间可以在生命周期结束后自动释放该怎么办呢?
什么是智能指针
智能指针是一个用于管理和存储指针的类,它利用对象出了作用域自动调用析构函数的特性帮助我们释放空间。接下来模拟实现一个简单的智能指针类。
#include <iostream>
#include <string>
#include <memory>
template<class T>
class SmartPtr
{
public:
SmartPtr(T* ptr)
:_ptr(ptr)
{
std::cout << "construct smartptr" << std::endl;
}
~SmartPtr()
{
if(_ptr)
{
delete _ptr;
}
std::cout << "destory smartptr" << std::endl;
}
private:
T* _ptr;
};
void Func()
{
std::string* ptr = new std::string;
SmartPtr<std::string> smartPtr(ptr);
}
int main()
{
Func();
}
construct smartptr
destory smartptr
以上这个智能指针类就已经基本实现了帮助我们在指针声明周期结束后自动释放内存空间的功能,这种与之类似的功能实现在Cpp中称之为RAII(Resource Acquisition Is Initialization),资源获取即初始化
,是Cpp中常用的用于管理资源的方法,由这套方法产生了智能指针,当然还有智能锁,几乎所有我们在使用完必须释放资源的类型都可以使用这套方法进行资源管理。这套资源管理的方法使得我们不需要显示的释放资源,并且使资源在其生命周期内长期有效,出了作用域后自动帮助我们释放资源防止出现内存泄露的问题。
我们之前实现的只能发指针类并没有实现完全,因为作为智能指针我们必须要求它能够像指针一样进行使用。
#include <iostream>
#include <string>
#include <memory>
template<class T>
class SmartPtr
{
public:
//资源管理
SmartPtr(T* ptr)
:_ptr(ptr)
{
std::cout << "construct smartptr" << std::endl;
}
~SmartPtr()
{
if(_ptr)
{
delete _ptr;
}
std::cout << "destory smartptr" << std::endl;
}
//使其可以像指针一样使用
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
void Func()
{
std::string* ptr = new std::string("Hello");
SmartPtr<std::string> smartPtr(ptr);
std::cout << *smartPtr << std::endl;
std::cout << smartPtr->size() << std::endl;
}
int main()
{
Func();
}
construct smartptr
Hello
5
destory smartptr
这样就可以称为一个较为完整的智能指针了,但是单单这样还不够,因为这个类中最重要的也是最难处理的两个默认函数还没有书写,即拷贝构造函数和赋值运算符重载函数,这两个函数的处理也苦恼了Cpp标准库中的多种智能指针,接下来我们就要着重讨论标准库中对这两个函数的处理。
库中的智能指针
早在C++98
的版本中就已经出现了智能指针,它叫auto_ptr
,但是由于十分不好用于是后面在C++11
中出现了unique_ptr
和shared_ptr
。不同的智能指针除了拷贝构造和赋值运算符重载函数的实现外其他都是大同小异,我们这里着重讨论他们在拷贝构造函数和赋值运算符重载函数上的不同。
为什么不同的智能指针在这两个默认成员函数上实现区别会很大呢?我们稍微思索即可发现,指针的拷贝和赋值往往和资源何时释放而挂钩。如果一个智能指针A拷贝了另一个智能指针B,而当B出了声明周期此时要不要释放资源呢?如果释放资源那么我们再次使用指针A就会有未定义行为的发生。但如果不是放那么何时又该释放资源呢?这些问题的处理上使得智能指针有了差别。
auto_ptr
auto_ptr
诞生在C++98
的标准库中,但是这也是问题最多的智能指针,他对拷贝构造即赋值运算符重载的处理我们可以用一句话总结,即管理权限转移,我们以下作个简单的例子。注意:auto_ptr
在C++11
中已经被删除,因此我们可以依照文档自己实现一个进行试验。
#include <iostream>
#include <string>
#include <memory>
template<class T>
class auto_ptr
{
public:
//资源管理
auto_ptr(T* ptr)
:_ptr(ptr)
{
std::cout << "construct smartptr" << std::endl;
}
~auto_ptr()
{
if(_ptr)
{
delete _ptr;
}
std::cout << "destory smartptr" << std::endl;
}
//使其可以像指针一样使用
T& operator*()
{
return *