目录⭐️1现在,我们已经结束了C++的一个小部分,我还要给大家介绍一些C++在C语言中做的一些补充。
⭐️博客代码已上传至gitee:https://gitee.com/byte-binxin/cpp-class-code
C/C++的内存分布C语言中的动态内存管理C++内存管理方式C和C++在内存申请失败时处理方式的区别operator new和operator delete函数new和delete的实现原理定位new表达式malloc/free和new/delete的区别内存泄漏总结
C/C++的内存分布
C语言中的动态内存管理C/C++内存有六个区域 ,我们经常听到的有栈、堆、数据段和代码段。还有两个分别是内核空间和内存映射段。
下面是几个说明:
栈:向下增长,非静态局部变量,函数返回值,参数列表,函数栈帧等(一般8M)
堆:向上增长,动态内存分配,手动申请
数据段:存储静态数据和全局数据
代码段:可执行代码,只读常量
C++内存管理方式前面有博客讲到过,有兴趣可以去看一下:C语言之动态内存管理
方式: 通过new和delete *** 作符进行动态内存管理。C++可以继续使用C的内存管理方式,也可以用自己的内存管理方式。
下面用这两个 *** 作符来处理自定义类型和内置类型,并且和C的内存管理方式的效果进行对比。
- 内置类型
int main() { int* p1 = (int*)malloc(sizeof(int)); int* p2 = (int*)malloc(5 * sizeof(int)); // 对于内置类型,malloc和new没有本质的区别,只是用法不同 // new 和 delete都是 *** 作符 int* p3 = new int; // int* p3 = new int(3); int* p4 = new int[5]; // int* p4 = new int[5]{ 1,2,3,4,5 }; C++98不支持初始化new的数组,C++11支持 free(p1); free(p2); delete p3; delete[] p4; p1 = nullptr; p2 = nullptr; p3 = nullptr; p4 = nullptr; return 0; }
对于内置类型,malloc和new没有本质的区别,只是用法不同
通过调试发现,对于内存类型,两者都不会对空间初始化,但是new可以通过一定的手段来进行初始化。int* p3 = new int(3); int* p4 = new int[5]{ 1,2,3,4,5 }; C++98不支持初始化new的数组,C++11支持上面两种方式都是可以在开空间时,给它们来进行初始化。但是C语言中的malloc不可以。
- 自定义类型
class A { public: A(int a = 10) :_a{ a } { cout << "A(int a = 0)" << endl; } private: int _a; }; void test() { // 对于自定义类型,new会调用构造函数,delete会调用析构函数,而malloc和free不会 A* p1 = (A*)malloc(sizeof(A)); A* p2 = (A*)malloc(sizeof(A) * 5); // new在堆上申请空间,然后调用构造函数初始化 // delete先调用析构函数,然后释放空间给堆上 A* p3 = new A; A* p4 = new A[5]; // A* p4 = new A[5]{ 1,2,3,4,5 }; free(p1); free(p2); delete p3; delete[] p4; p1 = nullptr; p2 = nullptr; p3 = nullptr; p4 = nullptr; }
对于自定义类型,new会先开空间,然后调用构造函数进行初始化,delete会调用析构函数来清理,然后释放空间,而malloc和free不会。观察下面的图片也可以发现。
总共有6次构造函数的调用。
总结:
- 对于内置类型,malloc和new没有本质的区别,只是用法不同对于自定义类型,new会调用构造函数,delete会调用析构函数,而malloc和free不会
面向对象的语言,处理错误的方式一般是抛异常,C++也是这样要求的 ( try catch)
面向过程的语言,处理错误的方式一般是返回值+错误码
- C语言
int main() { size_t sz = 2; int* p1 = (int*)malloc(1024 * 1024 * 1024 * sz); if (p1 == nullptr) { printf("%dn", errno);// errno-错误码 perror("malloc fail"); exit(-1); } printf("%p", p1); free(p1); return 0; }
运行结果如下:
我们可以发现,内存申请失败了,程序返回了错误码:12,原因是系统没有这么大的空间。
- C++
int main() { size_t sz = 2; int* p1 = new int[1024 * 1024 * 1024 * sz]; delete[] p1; return 0; }
运行结果如下:
程序直接崩溃。
下面我们对错误进行抛异常处理:
int main() { size_t sz = 2; char* p1 = nullptr; try { // 进行检测 有错误就进入catch被铺获,try中错误代码后面也不执行 p1 = new char[1024 * 1024 * 1024 * sz]; cout << "检测无误" << endl; } catch (const exception& e) { cout << e.what() << endl; } printf("%p", p1); delete[] p1; return 0; }
operator new和operator delete函数
new和delete是用户进行动态内存申请和释放的 *** 作符,operator new 和operator delete是系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数来释放空间。
operator new
operator new是通过malloc来开空间。相比malloc多了一个抛异常的功能。
总结:
operator new = malloc + 失败抛异常处理new = operator new + 构造函数
operator delete
new和delete的实现原理operator delete与delete无区别,它的出现主要是为了和operator new成对出现
- 内置类型
如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL。
- 自定义类型
new的原理
先调用operator new函数申请空间,然后调用构造函数进行初始化delete原理
先调用析构函数清理资源,然后调用operator delete函数释放空间new T[]原理
先调用operator new[]函数完成N个对象的空间的申请,然后调用N次构造函数进行初始化delete[] 原理
先调用N次析构函数完成N个对象中资源的清理,然后调用operator delete[]函数释放空间
定位new表达式
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式: new(空间指针)类型(类型初始化列表)
class B { public: B(int a = 0, int b = 0) :_a(a) ,_b(b) {} private: int _a; int _b; }; int main() { B* b = new B; new(b)B(2, 3);// 有参传参,无参不传 return 0; }malloc/free和new/delete的区别
从三个角度分析如下:
- 概念性质: malloc/free是函数,new/delete是 *** 作符使用方法: malloc需要手动计数申请空间的大小且需要将void*类型强转为对应类型,new后面跟着的是类型,只需要按类型申请即可。使用效果: malloc申请的空间不会进行初始化,且申请失败是返回NULL,new申请的空间可以初始化,对应自定义类型会调用它的构造函数进行初始化,delete在释放空间之前会调用析构函数来清理资源。
概念: 内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
注意: 内存泄漏对短期的运行程序危害不大,对长期运行的程序影响很大,会导致系统卡死等。
总结今天就简单介绍了C/C++中内存管理的相关知识,喜欢的话可以点赞和关注支持一下,欢迎大家点赞支持~
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)