C++动态内存管理

2021-02-20 14:36发布

1.内存划分

  • 程序运行以后,内存划分的区域——(1)内核空间:放置操作系统相关代码和数据,用户不能直接进行操作,必须通过系统调用函数。(2)栈区:存放函数,寄存器中开辟的变量,包括函数参数,返回值。(3)内存映射段:存放文件映射,库,匿名映射等。(4)堆区:存放动态开辟的变量,如malloc,new等。(5)数据段(分为全局变量区和静态区):存放全局变量以及static修饰的变量。(6)代码段:程序中的代码以及常量。
  • 为什么要进行分区——方便数据的管理。

2.C语言中的动态内存管理函数

  • malloc——void* malloc(size_t size),size:要申请空间的字节数。
  • calloc——void* calloc(size_t n,size_t size),n:要设置的元素个数,size:每个元素大小。calloc会对申请的内存进行初始化。
  • realloc——void* realloc( void *mem_address, size_t newsize ),将mem_address的内存空间改为newsize,如果mem_address为空,则跟malloc使用大致相同,如果newsize比旧的空间要小,则原地减小空间,指针指向不变。如果newsize比oldsize大一点,则看该内存空间末尾后有没有可用的空间,如果有,则使用,指针指向不变。如果newsize较大,后面没有多余的空间,则新开辟一段内存,将原来的内存空间的内容拷贝到新的空间,然后释放旧空间,返回新空间。

3.malloc/free 与 new/delete的区别

  • 相同点——都从堆上申请空间,并且需要用户手动释放。
  • malloc和new的不同点——(1)new是操作符,malloc是库函数。(2)malloc返回void*,需要用户对类型进行强转,new不需要。(3)malloc需要用户给出开辟多大的空间,new会自动计算类型的大小。(4)malloc申请的空间不会进行初始化,new申请的空间可以进行初始化。(5)malloc申请空间失败时返回NULL,new失败抛出异常。

4.new,new[],delete,delete[]内部实现

  • new,delete的内部实现——new在内部使用malloc申请内存,但是是循环申请,申请成功则返回,失败则进行内存操作,争取申请到内存,实在不行则抛出异常。参数同样是size_t count,传入字节数,但是这个参数编译器会自动计算然后传递。在申请完空间以后,会调用类的构造函数进行初始化。delete:会先调用析构函数进行清理资源,清理完毕以后再调用free释放空间。
  • new[],delete[]——new[]在底层调用new,一次性传入要申请的内存大小字节,如果显示定义了析构函数,还会在原来的基础上新增4个字节。然后再调用数次构造函数来对内存空间进行初始化。delete[]同样调用delete,先调用数次析构函数,然后再释放空间。

5.malloc/free, new/delete, new[]/delete[]不配套使用的问题。

(1)如果使用的类型是内置基本类型,则这三者混合使用将不会出现内存泄漏以及程序崩溃或者异常中断的问题。

(2)如果使用的类型是类类型,但是类中没有动态申请的内存资源,且类中没有显示定义析构函数,则三者混用不会出现内存泄漏,程序异常崩溃的问题。但是如果类中显示定义了析构函数,则new/delete与new[]/delete[]混用会导致程序崩溃。但是此时用malloc申请内存,delete释放不会出现问题,delete[]释放会出现问题。

(3)如果使用类类型,类中有申请堆上资源的变量,类中没有显示定义析构函数。使用malloc申请内存,delete,delete[]释放不会导致内存泄漏等问题,原因在于malloc不会调用构造函数,因此不会在构造对象时动态申请内存空间,也就不需要释放。但是如果显示定义了析构函数,则delete与delete[]都会导致程序抛出异常。使用new,new[]申请内存,free释放,会导致内存泄漏,但是不会造成异常。在显示定义了析构函数的情况下,newdelete家族混合使用会导致异常。

标签: