c、c++总结——动态内存管理

在介绍动态内存管理之前,让我先啰嗦几句关于内存的问题。经常写代码的同学都知道,内存我们主要分为三个:

静态区、堆和栈

静态区主要存储一些静态全局变量。静态区的内容在整个程序运行期间一直存在,在程序运行结束时销毁。由于它在程序运行之            前就已经明确,因此程序是没有办法对他进行修改的。

又叫堆栈。它主要存储的是一些局部变量和函数形参。在函数运行结束后就立即被销毁。堆栈有一个显著的特点就是:效率高            但是空间并不大。

:由malloc/new分配的一段空间。该空间需要free/delete手动释放,否则容易造成内存泄漏。它相对于堆栈来说。可使用的空间大得多,但是由于经常会引起内存泄漏的问题,所以并不安全。




典型的内存泄漏的几种情况:

a.内存还没有申请成功,就开始使用

b.内存申请成功,但没有初始化

c.内存使用后没有进行释放

d.只释放了一部分内存




同样是开辟一段内存空间,malloc/calloc/ralloc之间的区别是微妙的:

void* malloc(unsigned size);

malloc在堆上开辟一段大小为size字节的连续的内存空间。

void* calloc(size_t n, size_t elsize);

 calloc分配n个大小为size字节的内存空间,并将每个空间的内容都初始化为0.

void* realloc(void* ptr, unsigned newsize);

realloc将原来ptr里的内容释放掉,然后分配newsize个字节大小的空间,最后把原来的数据拷贝到新分配的内存空间里。





接下来,我们再来看看malloc/free和new/delete之间的区别:

a. malloc/new是标准c语言的两个库函数。new/delete是c++中的两个操作符。它们的作用都是动态的管理一段内存空间。

对于非标准数据类型的数据而言,他在创建的时候要调用构造函数,在释放的时候要调用析构函数。由于编译器无法对函数体内部进行管理,此时,malloc/new就不能达到要求了。这时候就需要new/delete来替我们完成构造和析构的工作。

b. malloc申请的空间需要我们手动计算所需要的大小。而new则会由系统自行计算所需要的内存大小。

c. new操作符是类型安全的,返回的是对象类型的指针,严格与对象匹配,无需进行类型转换。malloc分配成功返回的是void*,这就需要我们通过强转来将void*转换成我们需要的类型。

int* p1 = (int*)malloc(sizeof(int));  
int* p2 = new (int);

d. malloc分配失败后会返回NULL,而new分配失败后会抛出异常,并不会返回NULL。

我们在c语言部分的代码习惯是在malloc之后用NULL与内存空间进行比较,不为空则分配成功。但这个习惯到了new这里却并没有什么用。因为程序如果能往下执行就说明已经分配成功了,否则早就抛异常了。这时候,如果我们想要看new是否成功的分配了空间就要用到异常机制。

e. new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会

new/delete来动态开辟数组时,int* p = new int[6];  将调用6次构造函数。。。同样的,delete[] p也将调用6次析构函数。

使用new操作符来分配对象内存时会经历三个步骤:

  • 第一步:调用operator new 函数(对于数组是operator new[])分配一块足够大的,原始的,未命名的内存空间以便存储特定类型的对象。
  • 第二步:编译器运行相应的构造函数以构造对象,并为其传入初值。
  • 第三部:对象构造完成后,返回一个指向该对象的指针。

使用delete操作符来释放对象内存时会经历两个步骤:

  • 第一步:调用对象的析构函数。
  • 第二步:编译器调用operator delete(或operator delete[])函数释放内存空间。

f.new/delete可以被重载,但是malloc/free不可以。

g. malloc/free、new/delete、new[]/delete[]一定要配对使用,否则也会造成内存泄漏。




猜你喜欢

转载自blog.csdn.net/weixin_36229332/article/details/80225571