1. 前言
这依然是面试的一大考点。(好吧,怎么又是面试呢?我说就不能说点对技术有用的东西吗?)
不能,开玩笑的,其实智能指针真的很方便了C++开发人员。
好吧,说到智能指针,还是先从c++常见的内存问题说起吧。
2. 内存问题
(1) 缓冲区溢出问题
说的简单一点,就是你申明了一个64长度的数组,却要访问并修改第65位,虽然有时候是不会出现问题,但是实际上,这个地址根本不知道里面放着什么东西,可能是函数的返回值?可能是某些重要的数据?可能是下面需要用到的数组?总之,这种情况造成的后果是不可估量的。
(2) 空悬指针/野指针
空悬指针/野指针是什么东西呢?举个简单的例子,比如有两个指针,都指向堆上相同的一个对象Object,然后这个两个指针分别位于不同的线程中,这样的话第一个线程肯定不知道第二个线程做了什么?第二个线程也不知道第一个线程有没有对该堆上数据做什么修改?
假如现在p1释放了这个堆上的内存,那么p2就成了野指针/空悬指针,因为它所指向的东西是不存在的。样例图如下:
p1,p2同时指向堆中Object对象
p1释放了资源,p2就成了空悬指针/野指针
这就是空悬指针/野指针
(3) 重复释放
这个问题和上面那个很像,只是当p2指向的东西被p1释放的时候,p2再去释放一次。
(4) 内存泄漏
这个问题是最常见的问题,当然,也不怎么好解决。但并不是那么严重的问题,毕竟比起缓冲区溢出,重复释放这种会导致系统奔溃的问题而言,内存泄漏其实并不算大问题,但也是一个常见的问题,毕竟随着使用的时间的增加,内存的增加也成线性,一定时间时候服务器的内存就会被占满,这不符合使用服务器的使用。
下面举个比较常见的例子:
char* IntToString(int n)
{
char *ch = new char[33];
int flag = 1,num = 0;
if(n < 0 )
{
flag = 0;
num++;
n = -n;
}
while(n)
{
ch[num++] = n%10 + '0';
n = n/10;
}
for(int i=0;i<num/2 && flag;i++)
swap(ch[i],ch[num-i-flag]);
for(int i=1;i<=num/2 && !flag;i++)
swap(ch[i],ch[num-i]);
if(!flag) ch[0] = '-';
ch[num] = '\0';
return ch;
}
上面的代码是一个整数变成字符串的例子,很容易理解,乍一看,觉得好像并不存在BUG,这和内存泄露有什么关系,但是仔细想一想,如果有那个客户端的小伙伴不小心使用了以下的代码:
cout << IntToString(100) << endl;
是不是就出事了,咦,创建的ch指针里面的内存去哪了?没错,这个时候无论怎么做,都没法释放你之前申请的内存。(这里可以使用string模板类来解决或者使用智能指针了来解决。)
(5) 不匹配的new[] / delete
这个问题,感觉没什么意义!在《Linux多线程服务器编程 使用moduo C++网络库》中说道,尽量不要自己使用delete,delete应该是交给资源管理者而不是申请资源的人。当然,也不是一定不能用。那会出现什么问题呢?我先简单说一下,就是new[]的时候你分配的是一个数组,这个数组包含了数组的大小以及数组里面的元素空间,使用delete的时候,delete执行的步骤是释放掉数组的大小以及数组的第一个元素,如果你数组原来存放的是类似String这样的东西,除了第一个,后面的析构函数是不会调用的,也就是你申请的内存统统都不会回收,这实际上也是一种内存泄漏的问题。
(6) 内存碎片<