一、复习C的内存管理
在学习C语言时,我们学习了C语言的内存分配,同时学习了C语言的内存管理函数malloc、calloc、realloc。这三个函数都用来从堆上分配空间,但有略微区别:
- malloc:
负责开辟开辟空间(按字节开辟)。
void *malloc( size_t size );
- calloc:
在开辟空间的同时对空间内数据进行初始化,初始化为0。
void *calloc( size_t num, size_t size ); //第一个参数是元素的个数,第二个参数是每个元素的大小
- realloc:
在空间已开辟的情况下(第一个参数不为空),进行空间的扩容;
若第一个参数为空,功能同malloc一致,负责开辟空间;
若要进行扩容的空间周围没有足够的连续空间,该函数负责在另外一段区域上重新开辟出一段空间,将就空间上的数据复制过来后,释放就空间。
void *realloc( void *memblock, size_t size );
利用这些函数开辟的空间是在堆区开辟的,如果不释放,在程序运行时就一直被占用着,很可能会造成内存泄漏问题,这时候,就要用到free函数来释放不继续使用的空间了。
void free( void *memblock );
二、C++中的内存管理
在C++中引入了类的概念,如果申请一段自定义的类的空间,相当于创建了一个或多个自定义对象,既然是创建自定义对象,就要自动调用构造函数,在释放该空间时,也要自动调用析构函数。C++中用new和delete来动态管理内存,它们可以自动的调用构造函数和析构函数。
new/delete动态管理对象;
new[]/delete[]动态管理对象数组;
【malloc/free和new/delete的区别和联系?】
1. 它们都是动态管理内存的入口。
2. malloc/free是C/C++标准库的函数,new/delete是C++操作符。
3. malloc/free只是动态分配内存空间/释放空间。而new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理(清理
成员)。
4. malloc/free需要手动计算类型大小且返回值会void*,new/delete可自己计算类型的大小,返回对应类型的指针。
注:在使用函数或操作符进行动态内存管理时,一定要配对使用。(malloc/free new/delete new[]/delete[])
如果没有配对使用,可能会造成内存泄漏甚至崩溃。
为什么一定要配对使用,造成内存泄漏甚至崩溃的原因是什么呢?让我们先探索下new和delete的实现:
然后我们来看不配对使用内存管理函数和操作符为什么会造成内存泄漏甚至程序崩溃:
//假设AA是一个结构体,此时程序不崩溃,但如果该结构体初始化时有在堆上申请空间的话,会造成内存泄漏
AA* p0 = new AA;
free(p0);
//程序崩溃,在申请空间时并没有申请多开辟4个字节,但在释放时认为最开始的四个字节为数组元素个数,
//释放的是从第5个字节开始的空间(释放错位置),导致程序崩溃
AA* p1 = new AA;
delete[] p1;
//同样是释放错位置导致的程序的崩溃
AA* p2 = new AA[10];
free(p2);
//同样是释放错位置导致的程序的崩溃
AA* p3 = new AA[10];
delete p3;
可以看出,不配对使用内存管理函数和操作符,可能会造成释放位置不对,从而导致内存崩溃;也可能会因为没有调用析构函数导致内存泄漏。
但是,对内置类型来说,程序是不会崩溃的,这是为什么呢?
内置类型不需要调用析构函数,因此,编译器会自动识别需不需要调用析构函数,如果可调可不调,就不会多开辟4个字节来存放count。
所以,对于没有显示写出析构函数的自定义类型,也不会崩溃,因为编译器会进行优化,不会开辟字节存放count,自然也就不会有释放位置的问题了。