菏泽市网站建设_网站建设公司_GitHub_seo优化
2026/1/7 8:25:06 网站建设 项目流程

复习C语言中的动态内存管理方式

void test1() { int* p1 = (int*)malloc(sizeof(int)); free(p1); // 1.malloc/calloc/realloc的区别: // malloc - 只分配内存,不初始化 // calloc - 分配内存并初始化为0,参数是(元素个数, 每个元素大小) // realloc - 重新分配内存,可以扩大或缩小已有内存块 int* p2 = (int*)calloc(4, sizeof(int)); // 分配4个int并初始化为0 int* p3 = (int*)realloc(p2, sizeof(int) * 10); // 将p2的内存扩大到10个int // 这里不需要free(p2)! // 因为realloc成功后,原来的p2指向的内存已经被释放或移动 // 如果realloc返回新指针,原指针p2不应该再被使用 free(p3); // 只需要释放新的指针p3 }

malloc是分配内存,不会初始化

calloc是分配内存初始化为0,参数(元素个数,每个元素的大小)

realloc是重新分配内存,可以扩大或者缩小已有的内存

在C++中兼容我们的C语言,所以C语言的动态内存管理在C++也可以使用

学习C++中的动态内存管理的方法

C语言内存管理方式在C++中可以继续使用,但是有一些地方不一定能做到,并且使用起来会有一些麻烦,因此,C++引出了自己的内存管理方式:通过new和delete操作符进行动态内存管理

但是,我们需要注意的是,malloc/free是库函数,而new/delete是操作符

void test1() { //C语言 malloc等是库函数 int* p1 = (int*)malloc(sizeof(int)); free(p1); //C++ new/delete是操作符 // 动态申请一个int类型的空间 int* ptr4 = new int;//写法 // 动态申请一个int类型的空间并初始化为10 int* ptr5 = new int(10);//写法 delete ptr4; delete ptr5; }

那么,new/delete和malloc/free有没有什么区别呢?

如果动态申请的对象是内置类型,那么用malloc和new没有什么区别,如果动态申请的对象是自定义类型,那么就有区别,比如

class A { public: A(int a=0) :_a(a) { cout<<"A()"<<endl; } ~A() { cout<<"~A()"<<endl; } private: int _a; }; int main() { //C语言自定义类型开辟空间 A* p3 = (A*)malloc(sizeof(A));//没有初始化 free(p3); //C++自定义类型开辟空间 A* p4 = new A;//调用构造函数初始化 delete p4;//调用析构函数 }

new和delete不仅仅会开空间和释放空间,还会调用构造函数和析构函数

接下来我们用数据去看,malloc和new的区别:

我们使用malloc没有初始化

使用了new进行了初始化

那我们想给new的对象传参应该怎么写呢?

A* p4 = new A(10);//调用构造函数初始化 //我们也可以new数组: A *ptr1 = new A[10];//new了10个对象,调用了10次构造函数 delete[] ptr1;

需要注意的是:在new数组的时候,delete需要+[]

总结:

对于内置类型用malloc和new没有什么区别,但是使用自定义类型就有区别,用malloc和new的区别是:new和delete会去调用构造函数和析构含糊,一定要匹配的去使用,不然会造成崩溃,在C++中,也建议去使用new和delete,malloc和free能做到的,那么new和delete也能做到,相反,new和delete能做到的,malloc和free不一定能够做到

operator new 与 operator delete函数

new和delete是用户进行动态内存申请和释放的操作符,operator new 和 operator delete 是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间.

注意:operator new 与 operator delete函数是全局的库函数,他们并不是new和delete的重载

我们使用new一个新对象T的时候,编译器有两个做法

1、申请内存,调用operator new(底层其实是将malloc封装)

2、调用库函数

在delete的时候,编译器会发生

1、调用T的析构函数

2、调用operator delete(底层其实是将free封装)

我们平时写malloc,会这样子去写

int main() { //malloc失败,返回NULL char* p1 = (char*)malloc((size_t)2*1024*1024*1024); if(p1==NULL) { printf("malloc fail\n"); } else { printf("malloc success\n"); } return 0; }

那么new操作符在new失败的时候会怎么去处理呢?

int main() { char* p2 = new char[0x7fffffff]; //并没有执行 if(p2==NULL) { printf("new fail\n"); } else { printf("new success\n"); } return 0; }

可以看到,new失败的时候,并没有去执行if语句

我们说过,malloc和new不一样,new在申请空间失败的时候,会抛出异常,下面我们看一段抛出异常的代码

int main() { try { void* p1 = new char[1024 * 1024 * 1024]; cout << p1 << endl; void* p2 = new char[1024 * 1024 * 1024]; cout << p2 << endl; void* p3 = new char[1024 * 1024 * 1024]; cout << p3 << endl; } catch (const exception& e) { cout << e.what() << endl;//抛出异常 } return 0; }

operator new其实就是对malloc的封装,如果申请内存失败了,抛出异常,封装malloc+抛出异常

operator delete也是对free进行封装

通过上述两个全局函数的例子

我们可以发现

operator new实际也是通过mallloc来申请空间,如果malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施就继续申请,否则抛出异常。operator delete 最终是通过free来释放空间的

new和delete的实现原理

内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不一样的地方是:new/delete申请释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败的时候会抛出异常,malloc会返回NULL

自定义类型

new的原理

1、调用operator new函数申请空间

2、在申请的空间上执行构造函数,完成对象的构造

delete的原理

1、在空间上执行析构函数,完成对象中资源的清理工作

2、调用operator delete 函数释放对象的空间

new T[N]的原理

1、调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请

2、在申请的空间上执行N次构造函数

delete[]的原理

1、在释放的对象上执行N次析构函数,完成对N个对象的资源清理

2、调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

int main() { Stack st; Stack* ps = new Stack; delete ps; retunr 0; }

定位new表达式(placement-new)

定位new表达式是在已分配的原始内存空间中调用构造函数初始一个对象

使用格式:

new (place_address) type或者new(place——address)type(initializer-list)

place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景:
定位new表达式在实际中一般是配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。

构造函数调用什么时候自动调用呢?

1、创建对象时

2、new一个对象时

class A { private: int _a; public: A(int a = 0)//构造函数 :_a(a) { cout << _a << ":" << "A(int a = 0)构造函数" << endl; } ~A()//析构函数 { cout << _a << ":" << "~A()析构函数" << endl; } }; int main() { A* ptr1 = (A*)malloc(sizeof(A)); //显示调用构造函数用来初始化一块已经开辟好的空间 new(ptr1)A(10); //显示调用析构函数 ptr1->~A(); //释放ptr1指向的空间 free(ptr1); A* ptr2 = (A*)operator new(sizeof(A)); //显示调用构造函数用来初始化一块已经开辟好的空间 new(ptr2)A; //显示调用析构函数 ptr2->~A(); //释放ptr2指向的空间 operator delete(ptr2); return 0; }

ptr1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行,显式对一块空间调用构造函数初始化,new(ptr1) A;

如果我们复制一份a数组到另外一块空间b数组,怎么复制呢?

也许你想的是这样,通过一个循环来拷贝:

int main() { A a[5]; //复制一份a数组到另外一块空间b A* pb1 = new A[5]; for(int i =0;i<5;++i) { b[i]=a[i]; } cout<<endl; //代价大 构造+赋值 return 0; }

但是这样代价大,经过了构造+赋值重载,那么能不能直接构造呢?通过定位new表达式就可以直接构造:

int main() { A a[5]; //能不能直接构造? A *pb = (A*)malloc(sizeof(A)*5); for(int i =0;i<5;++i) { new(pb+i)A(a[i]); }//只有构造 return 0; }

常见面试题

malloc/free和new/delete的区别
1、特点和用法

malloc和free是函数,new和delete是操作符;malloc申请空间时,需要手动计算空间大小并传递,new只需在后面跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可;malloc的返回值是void*,在使用时需要强转,new不需要,因为new后面跟的是空间的类型

2、底层原理区别

申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数和析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

3、处理错误的方式

malloc申请空间失败时,返回NULL,因为使用时需要判空,new不需要,但是new需要捕获异常

内存泄漏
什么是内存泄漏呢?
在堆上申请了的空间,在我们不用了以后也没有释放,就存在内存泄漏,因为你不用了,也没有还给系统,别人也用不了。俗话说:占着茅坑不拉屎

那么我们如何预防内存泄漏呢?

1、智能指针

2、内存泄漏的检测工具

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询