为什么需要智能指针
我们知道,内存泄漏是一件非常严重的错误,这要求如果malloc内存就一定要有对应的free,new了内存也一定要有对应的delete。但是如果程序员忘记写上对应的释放空间的操作符或者函数,那么就会出现内存泄漏,或者程序员写上了,但是中间某个逻辑导致程序崩溃了,并没有走释放空间的语句,那么也会造成内存泄漏,因此,为了防止内存泄漏的发生,C++11中引入了智能指针来管理空间。
-
内存泄漏
什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。
void MemoryLeaks()
{
// 1.内存申请了忘记释放
int* p1 = (int*)malloc(sizeof(int));
int* p2 = new int;
// 2.异常安全问题
int* p3 = new int[10];
Func(); // 这里Func函数抛异常导致 delete[] p3未执行,p3没被释放.
delete[] p3;
}
- 内存泄漏分类
堆内存泄漏(Heap leak)
堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。
系统资源泄漏
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
智能指针的使用及原理
- RAII
RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:- 不需要显式地释放资源。
- 采用这种方式,对象所需的资源在其生命期内始终保持有效。
利用上述RAII思想先写一个类似智能指针一样的类如下:
template<class T>
class smartptr
{
public:
smartptr(T* ptr = nullptr)
:_ptr(ptr)
{
cout<<"smartptr(T* ptr = nullptr)"<<endl;
}
~smartptr()//析构时释放空间
{
cout<<"~smartptr()"<<endl;
delete _ptr;
}
private:
T* _ptr;
};
int main()
{
smartptr<int> sp(new int(1));
cout<<"in main()"<<endl;
}
结果如下图:
即使我没有delete这个空间,但是通过smartptr这个类进行管理,则能够正确的释放这块空间,不过上面这个类还不是一个完整的智能指针,因为指针还应该具解引用和->操作去访问所管理空间的内容因此只需加以改造就可以实现一个最简单的智能指针。
template<class T>
class smartptr
{
public:
smartptr(T* ptr = nullptr)
:_ptr(ptr)
{
cout<<"smartptr(T* ptr = nullptr)"<<endl;
}
~smartptr()//析构时释放空间
{
cout<<"~smartptr()"<<endl;
delete _ptr;
}
T*operator ->()
{
return _ptr;
}
T&operator*()
{
return *_ptr;
}
private:
T* _ptr;
};
int main()
{
smartptr<int> sp(new int(1));
cout<<*sp<<endl;
cout<<"in main()"<<endl;
}
运行结果如下:
C++中提供的一系列智能指针
1.auto_ptr
C++98版本的库中就提供了auto_ptr的智能指针,用法如下:
class date
{
public:
date()
{
cout<<"date()"<<endl;
}
~date()
{
cout<<"~date()"<<endl;
}
public:
int _day;
};
void test_auto_ptr()
{
auto_ptr<date> ap(new date);
ap->_day = 100;
cout<<ap->_day<<endl;
}
运行结果如下: