写在前面
C++内存管理小节主要需要掌握的内容就是:
- 结合进程地址空间,理解不同对象存储区域。
- new/ delete malloc/ free的区别,从用法和实现两方面说。
- 什么是内存泄露,危害是什么,如何解决。
内存管理
地址空间
注意:
- 栈区又叫堆栈,储存非静态局部变量,函数参数,返回值等,栈区向下增长。
- 内存映射段是高效I/O映射方式,用于装载一个共享的动态内存库。用户可以使用系统接口创建共享内存,做进程间通信。
- 堆区主要用于程序运行时的动态内存分配,堆可以向上增长。
- 数据段存储全局数据和静态数据。
- 代码段存储可执行代码和只读常量。
小题:
int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
static int staticVar = 1;
int localVar = 1;
int num1[10] = { 1, 2, 3, 4 };
char char2[] = "abcd";
const char* pChar3 = "abcd";
int* ptr1 = (int*)malloc(sizeof(int) * 4);
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1);
free(ptr3);
}
1. 选择题:
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?____ 全局变量:静态区、数据段
staticGlobalVar在哪里?____ 静态全局变量:静态区、数据段
staticVar在哪里?____ 静态局部变量:静态区、数据段
localVar在哪里?____ 局部变量:栈区
num1 在哪里?____ 整形数组:栈区
char2在哪里?____ 字符数组:栈区
pChar3在哪里?____ 栈区的局部变量
ptr1在哪里?____ malloc的int*:堆区
*char2在哪里?___ *首元素地址 就是首元素
*pChar3在哪里?____ 代码段/常量区
*ptr1在哪里?____ 堆区
2. 填空题:
sizeof(num1) = ____; 40
sizeof(char2) = _____; 5 (包括'\0')
strlen(char2) = ____; 4
sizeof(pChar3) = ____; 4/8
strlen(pChar3) = ____; 4
sizeof(ptr1) = ____; 4/8
3. sizeof 和 strlen 区别?
4. C语言中的动态内存管理方法是:malloc、calloc、realloc、free。
请问 :malloc/calloc/realloc的区别
calloc会初始化,相当于,malloc+memset,按照字节初始化,空间每个字节都初始化为0; realloc扩容,原地扩/异地扩
C++内存管理方式
new 和delete 是用户进行动态内存申请和释放的操作符,C++通过new 和delete操作符进行动态内存管理。
- 申请和释放单个元素的空间,使用new 和delete操作符,申请和释放连续的空间,使用new[] 和delete[]。要匹配起来使用。
- 在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc 和free不会。
- operator new 和operator delete是系统提供的全局函数, new 在底层调用operator new 全局函数来申请空间,delete 在底层通过operator delete全局函数来释放空间。
- 而operator new:实际上通过malloc 来申请空间,当malloc 申请空间成功时直接返回;申请空间失败,尝试执行空间不足的应对措施;如果这个应对措施用户设置了,就继续申请,如果没有就抛异常。
- operator delete:实际上通过free 来释放空间。
-
如果申请内置类型的空间,new 和malloc,delete 和free 基本类似。不同点在于:new/delete 申请和释放的是单个元素的空间,new[] 和delete[] 申请的是连续的空间,而且new 在申请空间失败时会抛异常,malloc 会返回NULL。
-
如果申请自定义类型的空间:
- new 的原理:
- 调用operator new函数申请空间。(本质malloc)
- 在申请的空间上执行构造函数,完成对象的构造。
- delete 的原理:
- 在空间上执行析构函数,完成对象中资源的清理工作。
- 调用operator delete函数释放对象的空间。(本质是free )
- new T[N] 的原理;
- 调用operator new[] 函数,在operator new[] 中实际调用operator new函数完成N 个对象空间的申请。
- 在申请的空间上执行N 次构造函数。
- delete[] 的原理:
- 在释放的对象空间上执行N次析构函数,分别完成对N 个对象中资源的清理。
- 调用operator delete[] 释放空间,实际在operator delete[] 中调用operator delete 来释放空间。
- new 的原理:
-
定位new 表达式 (placement- new)
定位new 表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式:
new (place_address) type //or new (place_address) type(initializer-list) // place_address 是一个指针。 // initializer-list 是类型的初始化列表。
使用场景:
定位new 表达式在实际中一般配合内存池使用。因为内存池分配出的内存没有初始化,所以如果是自定义类型的对象,需要使用new 的定义表达式进行显示调用构造函数进行初始化。使用例子:
class A{ private: int _a; public: A(int a = 0) :_a(a) { cout << "A():" << this << endl; } ~A(){ cout << "~A():" << this << endl; } }; //USE: int main() { A* p1 = (A*)malloc(sizeof(A)); new(p1)A; p1->~A(); free(p1); A*p2 = (A*)operator new(sizeof(A)); new(p2)A(10); p2->~A(); operator delete(p2); return 0; }
malloc/free 和 new/delete的区别
先说共同点:都从堆上申请空间,并且由用户手动释放。
不同点:
- 用法:
- malloc、free是函数,new和delete是操作符。
- malloc申请的空间不会初始化,new 可以初始化。
- malloc申请空间时,需要手动计算空间大小并且传递,new只需要在后面跟上空间类型。如果是多个对象,[]中指定对象的个数即可。
- malloc返回值为(void*),在使用时必须强转,new不需要,因为new 后跟的是空间的类型。
- malloc申请空间失败时返回NULL,所以使用后必须判空。new不需要,但new要捕获异常。
- 实现:
- 申请自定义类型的空间时,malloc/free只会开辟空间,不会调用构造函数和析构函数;而new/delete可以在申请空间后嗲用构造函数完成对象的初始化/释放空间前调用析构函数完成空间中资源的清理。
内存泄漏
内存泄漏指的是因为疏忽或者错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,因而造成了内存的浪费。
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等,出现内存泄漏会导致响应越来越慢,最终卡死。
举例:
void MemoryLeaks(){
//1. 内存申请了而不释放
int* p1 = (int*)malloc(sizeof(int));
int* p2 = new int;
//2. 异常安全问题。
int* p3 = new int[10];
Func(); //如果Func函数抛异常,会导致delete[] p3不执行,p3不被释放。
delete[] p3;
}
内存泄漏分类:
- 堆内存泄漏:堆内存指的是程序执行中依据需要分配通过malloc/calloc/realloc/new 等从堆中分配的一块内存,用完后必须通过调用相应的free 或者delete 删除。如果程序设计错误导致这块内存没有被删除,就会产生堆内存泄漏。
- 系统资源泄漏:比如程序使用系统分配的资源,比如文件描述符,管道等没有使用对应的函数释放,导致系统资源的浪费,严重的可导致系统效能减少,系统执行不稳定。
解决内存泄漏:
1.事前预防,如使用智能指针。2.事后纠错,用泄漏检测工具查询。
本小节完。