【C++】C++动态内存管理

️‍C/C++内存分布

【C++】C++动态内存管理_第1张图片

说明:

我们的代码并非放在代码段里的,而是以文件的形式存在磁盘上的。

代码经过编译链接形成的二进制指令,才是放进代码段里的。(即可执行代码)

练习:

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";
 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);
}

(选择题) 选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)

globalVar在哪里?

staticGlobalVar在哪里?

staticVar在哪里?

localVar在哪里?

num1 在哪里?

char2在哪里?

*char2在哪里?

pChar3在哪里?

*pChar3在哪里?

ptr1在哪里?

*ptr1在哪里?

答案:

CCCAA

AAADAB

解释这里的易错题:*pchar3在哪里?

char2和pchar3都是局部变量,放在栈区,这个好理解。"abcd"本身是常量字符串,无论其有没有被const修饰。

char2[]是一个数组,编译器将"abcd"拷至这个数组里,所以说,char2[]里的abcd是可修改的,是随着char2数组一起放在栈区的。

而pchar3指向的是原本的abcd,即放在代码区的常量字符串。所以,*pchar3是位于代码区的。

️‍C++的内存管理方式

new/delete

C++在原有基础上(malloc之流) 引入了new和delete。

new的用法:

//动态申请一个int类型的空间
int*p1 = new int;
​
//动态申请一个int类型的空间,并初始化为10
int*p2 = new int(10);
​
//动态申请10个int类型的空间
int*p3 = new int[10];
 
//动态申请10个int类型的空间,并将前三个分别初始化为1、2、3
int*p = new int[5]{1,2,3};   //注意:这种方式C++98是不支持的,C++11支持

delete的用法:

delete p1;
delete p2;
delete[] p3;
​
//注:下面这种写法是错误的
delete p1,p2;   //这样写仅能delete p1

delete是一个操作符,后面直接跟操作数,不需要像free那样接 ( )。

【C++】C++动态内存管理_第2张图片

之前C语言中学到的malloc、calloc、free等,C++中照样是可以用的。

❓那么问题来了:new/delete 和 malloc/free 的区别是什么?

区别一:在对待自定义类型上

其实两者对待内置类型没有区别。

但是对于自定义类型,new /delete 除了开空间还会调用构造函数和析构函数。

可见,new/delete是为自定义类型准备的。

区别二:开辟失败时

malloc申请空间失败会返回NULL,所以需要进行检查

而new申请不需要做检查,因为申请失败它直接抛异常:

【C++】C++动态内存管理_第3张图片

通过try-catch可以知道发生了什么异常:

【C++】C++动态内存管理_第4张图片

结果:bad allocation

【C++】C++动态内存管理_第5张图片

区别三:new/delete是关键字,而malloc/free是函数

几点说明

1.new和delete、new[]和detele[]一定要配起来使用,否则可能出问题。

如果这样写:

A*p=new A[3];
delete p;

虽然不会造成内存泄露,但程序可能会崩溃。

2.

A*p=new A[3];   //调用了3次A的构造函数
​
delete[] p;     //调用了3次A的析构函数

我们看看调用过程:

【C++】C++动态内存管理_第6张图片

new/delete操作自定义类型

class A
{
public:
    A() {
        cout << "A()" << endl;
    }
    ~A() {
        cout << "~A()" << endl;
    }
};
​
int main()
{
    A* p1 = new A;  //new自定义类型
    delete p1;
​
    A* p2 = (A*)malloc(sizeof(A));  //malloc自定义类型
    free(p2);
​
    int* p3 = new int;   //new内置类型
    delete p3;
​
    int* p4 = (int*)malloc(sizeof(int));  //malloc内置类型
    free(p4);
    return 0;
}

结果:

可见,只有new/delete在操作自定义类型时,会自动调用构造和析构函数。

new/delete的底层原理

new/delete的底层原理其实是 调用了函数operator new / operator delete来申请和释放空间。

注:这里不是运算符重载,函数名就是operator new。

️‍️我们来看一下函数operator new :

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{
 void *p;
 while ((p = malloc(size)) == 0)  //通过malloc申请
     if (_callnewh(size) == 0)
     {
         // 当内存不够,申请失败时
         // 这里会抛出bad_alloc 类型异常
         static const std::bad_alloc nomem;
         _RAISE(nomem);
     }
 return (p);
}
 
  

可见,operator new 实际也是通过malloc来申请空间,而operator delete是通过free来释放空间的。

既然本质还是用malloc,那兜这么一圈的意义何在?

开空间的同时封装了malloc,失败时能符合C++new的失败机制,即抛异常。

常考的笔试题

Q: malloc/free和new/delete的区别 ?

从本质上:1.new/delete是关键字,而malloc/free是函数。

从功能上:

2.malloc/free仅能开空间,而new/delete在开空间的基础上还能自动调用构造/析构函数。

3.malloc开空间时要算好具体开的字节数,而new不用,想开几个,[ ]中指定对象个数即可。

4.malloc的返回值为void*,在使用时必须强转,new不需要,因为new后跟的是空间的类型。

5.malloc申请空间失败时,返回的是NULL,因此使用时必须判空;new不需要,但是new需要捕获异常。

你可能感兴趣的:(c++)