林锐博士曾将内存管理比喻为“雷区”(《高质量C++/C编程指南》第44页),内存管理这块难不难?恐怕不好说。“会者不难难者不会”嘛。但是说内存管理这块难以成为“会者”,应该是没有错的。
程序时时刻刻与内存打交道,只不过以往我们不用考虑,甚至不用知道。所以,所谓“内存管理”,是特指堆内存。
如果把堆内存和栈内存的使用放在一起考虑,可以降低对内存管理恐惧。
一、内存的分配:
int i(100);//栈上分配内存
int *pi = new int(100);//堆上分配内存
以上两种分配,都使用了100作为初始值进行初始化,如果是进行类对象的分配,它们还可以指定使用哪个构造函数,比如:
CString s(s1);//栈上分配内存
CString *ps = new CString(s1)//堆上分配内存
这里的s可以是char*指针,也可以是另一个CString对象,它的类型将决定上面这两行语句调用哪一个构造函数。
在这里,有一点要特别说明,如果要使用默认构造函数,则new语句后面可以用空括号对,而栈内分配的语句切不可用空括号对。如果写成“CString s();”,则并不是定义一个CString对象s,而是定义一个返回值为CString的函数s。
上面两种分配,也都可以分配对象数组,不同的是,用new操作符在堆内存分配数组时,只能调用默认构造函数。而在栈上分配却可以指定对象成员的初始值。如:
int a[3] = {1,2,3};//栈上分配内存,int可以换成其它类型名,后面的初始值可作相应调整。
int *p = new int[3];//不能指定这三个对象的初始值
二、内存的访问:
栈内存可以通过对象访问,也可以通过指针访问,堆内存通过指针访问。方法完全相同。
三、内存的释放:
栈内存在对象作用域结束后自动释放,堆内存要用delete。
delete pi;//释放内存
delete []p;//释放对象数组
对于释放对象数组,那个空的[]对不可以丢,否则将只释放数组的第一个元素。导致内存泄露。
有了以上对比,堆内存似乎没有了任何难度。那么内存管理的玄机究竟在哪儿呢?在进行内存分配与释放的时候,有几个注意点要记住:
1、new操作有可能失败,当系统无法分配需要的内存块时,将返回NULL值,所以在new操作之后,要立即判断pi的值是否为NULL。
int *pi = new int(100);
if (pi = NULL) {...}
2、堆上分配的内存必须delete,而且只可以delete一次。为了保证内存只被delete一次,请务必记住delete以后立即将指针设为NULL:
delete pi;
pi = NULL;
虽然“pi=NULL;”不是必须的,但这是个好习惯。将指针设为NULL既可以防止继续读写该内存,也可以防止再次释放该内存。
老的C程序员可能忘不了malloc和free函数,它们也可以进行内存的分配与释放。但是C++时代它们已经落伍了。它们只是按请求的字节数进行分配,而不管你用这块内存来干什么。这样做,就等于放弃了类对象的构造与析构。对于很多类来说,这样做是很危险的。
本站文章皆为作者原创,其它媒体(包括但不限于报刊、杂志、网站、电视、电台)未经作者书面许可严禁转载(或部分摘录)!
