C++:new和delete

C++:new和delete

我们之前在学习C语言的时候,学习过了如何申请动态内存和释放内存。当时用的是C++/C语言的标准库函数malloc/free,而new / delete 是 C++的运算符。

 

我们先来看几个问题:

①:那这里我们就要想一下,有了 malloc/free,为什么还要 new/delete?

答:对于非内部数据类型的对象而言,光用 maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于 malloc/free。

因此 C++语言需要一个能完成动态内存分配和初始化工作的运算符 new,以及一个能完成清理与释放内存工作的运算符 delete。

 

注意 new/delete 不是库函数。

②:既然 new/delete 的功能完全覆盖了 malloc/free,为什么 C++不把 malloc/free 淘汰出局呢?

答:这是因为 C++程序经常要调用 C 函数,而 C 程序只能用 malloc/free 管理动态内存。

 

 

 

new的使用:

一:我们先用new创建简单的内置类型空间:

例如:我们申请一块存放int类型的动态内存

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

int *p2 = new int;

我们只需要在调用new的时候,将要申请的数据类型告诉new即可,new会自己在堆中找一块合适的内存块,并将该内存块的地址返回出来,我们只需要用合适的指针去接收即可。

这是因为 new 内置了 sizeof、类型转换和类型安全检查功能。

 

还有一个知识点:malloc 只能开辟内存,不能初始化

new   不仅可以开辟,还可以初始化

我们可以在声明的数据类型后面用小括号()进行赋值。当()中为空时则初始化为0

比如上述代码:int *p2 = new int;

我们可以申请的时候并将其赋值为10:int *p2 = new int(10);

 

我们还可以用 new 来创建常量内存块:const int *p = new const int(10);

不过我们需要注意的是:由于开辟的是常量内存块,所以const必不可少,另外一定得赋初值,因为只有这一次机会可以对其赋值,之后则不能修改其值,所以不赋初值则没有任何意义。

 

 

二:我们用new创建动态数组:

①:创建动态一维数组

 

我们先来创建普通内置类型的数组

例如:我们申请一块长度为length的int类型动态数组

int *p1 = (int *)malloc(sizeof(int) * length);

int *p2 = new int[length];

我们只需要在调用new的时候,将要申请的数组元素类型和长度告诉new即可。

这是因为 new 内置了 sizeof、类型转换和类型安全检查功能。

 

 

注意:当方括号‘[ ]’中为空时,默认为1:

 

 

这里我们也可以在申请动态内存块的时候进行初始化,不过只能一次性全部赋值为0,不赋值默认值则为随机值。

比如上述代码:int *p2 = new int[length];

我们可以申请的时候并将其全部赋值为0:int *p2 = new int[length]();

在用 delete 释放数组时,留意不要丢了符号‘[ ]’。

例如

delete [ ]p2;  // 正确的用法

delete p2;  // 错误的用法

 

 

当然如果用 new 创建对象数组,那么只能使用对象的无参数构造函数。

例如:

Obj *objects = new Obj[100]; // 创建 100 个动态对象

不能写成

Obj *objects = new Obj[100](1);// 创建 100 个动态对象的同时赋初值 1

 

在用 delete 释放对象数组时,留意不要丢了符号‘[ ]’。

例如

delete [ ]objects;  // 正确的用法

delete objects;  // 错误的用法

后者相当于 delete objects[0],漏掉了另外 99 个对象。

 

 

②:创建动态二维数组

和一维数组类似,这里我们创建10*10的动态二维数组:

我们这里可以利用二级指针申请一个二维数组:

int **a = new int *[10]();//注意这里int* 不可以加()

for(int i = 0; i < 10; i++)

{

a[i] = new int [10]();

}

 

运行结果:

 

也可以直接用数组指针的形式申请一个二维数组:

int (*a)[10] = new int[10][10]();

 

运行结果:

 

 

 

delete的使用:

 

一:当new申请的是简单的内置类型空间,则释放这块空间只需要在delete后面加上指向这块内存空间的指针即可。

例如:delete p;

      p = NULL;

 

为什么在delete掉指针p之后,还要将其置为NULL呢?

答:这里虽然会释放指针p所指向的内存,但是指针p还存在,不过指针p指向的内存已经不属于他了,那么,这时指针p就成为野指针了。

野指针定义:“野指针”不是 NULL 指针,是指向“垃圾”内存的指针 或者 指向随机内存的指针。

如果 p 是 NULL 指针,那么 free 对 p 无论操作多少次都不会出问题。

如果 p 不是 NULL 指针,那么 free 对 p 连续操作两次就会导致程序运行错误。

 

我们先来看delete之后,指针p的指向,和重复释放指针p指向的内存块的运行结果:

 

我们会发现野指针非常危险,所以我们一般在指针被free/delete之后,将其赋值为NULL。

 

 

二:当new申请的是动态一维数组空间,则释放这块空间只需要在delete后面加上‘[ ]’和指向这块内存空间的指针。//留意不要丢了符号‘[ ]’。

方括号[ ]的作用是告诉系统,现在要释放的是整个数组空间,不仅仅是数组第一个元素空间。

 

例如:

int *p = new int[10];

delete [ ]p;

p = NULL;

 

和上述问题一样,要防止出现野指针,在delete之后,将指针p置空并重复调用delete [ ]p

我们会发现,在free/delete之后将其指针置空还是非常有必要的!

 

 

三:释放二维数组

和上述一维数组的操作一致:①delete [ ]p;②p = NULL;

 

 

最后在这里,我们讲一下new的重定位:

我们先在栈上开辟了局部变量a的空间赋值为10;

再用new的重定位功能,在a的地址(&a)上征用char字节大小的空间赋值为‘a’;

 

我们试一试利用delete释放这块空间:

我们看到,这块内存是不可以利用delete释放的,因为这块内存是在栈上开辟的,是属于系统自动释放的,这块内存虽然被new征用,但不代表就可以利用delete释放掉。

 

 

 

最后我们看看使用new/delete时要注意的几点:

①:一定要配对的使用 new 和 delete ,否则将发生内存泄漏。

②:不要尝试释放已释放的内存块, C++标准指出,这样做的结果将是不确定的,意味着什么情况都有可能发生。所以我们要养成释放之后将其指针置空的习惯。

③:不要使用 delete 释放不是 new 分配的内存

④:如果使用 new[ ] 为数组分配内存,则应使用 delete[ ] 来释放,不要忘了‘[ ]’方括号

⑤:对空指针应用 delete 是安全的

 

 

new/delete和malloc/free的不同:

①:malloc 是函数, new 是关键字

②:new 比malloc多了 sizeof、类型转换和类型安全检查功能。

③:new 可以在开辟的同时进行初始化,而malloc不可以。

④:malloc失败会返回 NULL, new失败只会抛出异常

⑤:malloc无法直接开辟常量内存块,只能通过后面的指针来修饰;new 可以直接开辟常量内存块。

⑥:malloc在堆上开辟空间,new不一定是在堆上开辟(new可以重定位)

⑦:由于内部数据类型的“对象”没有构造与析构的过程,对它们而言 malloc/free 和 new/delete是等价的。

⑧:如果用 free 释放“new 创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用 delete 释放“malloc 申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。所以 new/delete 必须配对使用,malloc/free 也一样。

猜你喜欢

转载自blog.csdn.net/IT_Quanwudi/article/details/84587384