一、为什么需要智能指针
我们先来看一段代码
#include <iostream>
#include <vector>
#include <exception>
using namespace std;
void Test()
{
int* tmp = (int*)malloc(sizeof(int)* 100);
int* p = new int;
vector<int> v1(1000000000, 10);
vector<int> v2(10, 10);
//v2[10] = 20;
free(tmp);
delete p;
}
int main()
{
try
{
Test();
}
catch (const exception& e)
{
cout << e.what() << endl;
}
catch (...)
{
cout << "未知错误" << endl;
}
return 0;
}
问题分析:上面的问题分析出来我们发现有以下两个问题?
1.由于抛出异常之后的跳转, malloc和new出来的空间,没有进行释放,存在内存泄漏的问题。
2. 异常安全问题。如果在malloc和free之间如果存在抛异常,那么还是有内存泄漏。这种问题就叫异常安全,异常介绍可参见博客 C++ 异常
二、智能指针的使用及原理
2.1 RAII
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
- 不需要显式地释放资源。
- 采用这种方式,对象所需的资源在其生命期内始终保持有效。
//设计一个SmartPtr类
template<class T>
class SmartPtr
{
public:
SmartPtr(T* ptr = nullptr)
:_ptr(ptr)
{}
~SmartPtr()
{
if (_ptr)
delete _ptr;
cout << "~SmatrPtr()" << endl;
}
private:
T* _ptr;
};
void Test()
{
int* tmp = (int*)malloc(sizeof(int)* 100);
//将tmp指针委托给sp对象,让sp对象帮助tmp指针管理空间,直到tmp指针被销毁
SmartPtr<int> sp(tmp);
//vector<int> v(100000000, 10);
string s = "程序出错";
throw s;
free(tmp);
}
int main()
{
try
{
Test();
}
catch (string& s)
{
cout << s << endl;
}
catch (...)
{
cout << "未知错误" << endl;
}
return 0;
}
- 这里我们看到虽然在抛出异常后程序跳转至catch处,在抛出异常处后面的代码并未执行,但是由于将tmp的空间交给对象sp管理,在程序执行完之后,对象会自动调用析构函数清理资源,此时指针tmp的空间得到了正确的释放,这就是智能指针的用处。
- 两种情况都会调用析构函数:
- 1、程序正常走完,出了作用域,sp对象被销毁调用析构函数清理资源
- 2、程序抛出异常,跳出作用域到catch处,此时出了作用域sp对象被销毁调用析构函数清理资源