首页 > 编程笔记

C++ new和delete用法详解

在 C++ 中,指针变量的值是个地址,这个地址指向内存中的某一个区域,使用指针的时候,需要保证指向地址的有效性。因此,了解变量在内存中的存储方式,对于理解指针的生存周期有着非常重要的作用。

程序中内存的分配方式

在 C++ 程序中,内存分为 5 个区,分别是栈、堆、全局/静态存储区、常量存储区和程序代码区,程序中的各种数据都存储在这些内存区域中。

栈区(Stack)由编译器自动分配和释放,存放函数的参数以及局部变量。其分配运算内置于处理器的指令集中,效率很高。但是可使用的总量有限,一般不会超过 1M 字节。

堆区(Heap)中内存的分配和释放由开发者负责。一般用运算符 new 分配内存,并用运算符 delete 释放内存。一个 new 要对应一个 delete,否则会导致内存泄露。如果开发者没有释放,在程序结束的时候操作系统会自动回收。在堆上可分配的内存比栈上大了很多,且使用非常灵活。

全局/静态存储区中存放的是全局变量和静态变量。该存储区分配的内存在整个程序运行期间一直有效,直到程序结束由系统回收。

常量存储区中存储的是常量,通常不允许修改。在程序中定义的常量以及指针字符串都存储在这里。

程序代码区(Text Segment)用于存储程序的可执行代码。

对于初学者来说,经常混淆上面几种内存分配方式。下面有个简单的例子,使用了其中的几种内存分配方式:
#include <iostream>
int a;          // 全局变量,存放在全局/静态存储区
int main()
{
    int b;      // 局部变量,存放在栈区
    int* p = new int; // new 申请的堆区的内存空间;指针 p 是局部变量,存放在栈区
    const int num = 100; // 常量,存放在常量区

    delete p; // 释放堆空间
    return 0; 
}
在这 5 块内存区域中,栈区、全局/静态存储区以及常量存储区中的分配是由编译器来进行的,代码区由操作系统负责,只有在堆区的内存需要程序员手动管理。

本节主要介绍通过 new 和 delete 管理堆内存。

C++ new

在堆上分配内存,要使用 new 关键字,后面跟一个数据类型。如果需要对分配出的内存进行初始化,则在类型后面加上一个括号,并带有初始值。new 的语法格式如下:
new 类型(初值);
为了后续方便使用申请的堆内存,通常会使用一个指针指向 new 的结果,例如:
int *p = new int(5);
上面的语句在堆上分配一块整型变量的内存,并使指针 p 指向这块内存。括号中的 5 为这块内存提供一个初始值,该内存分配成功之后,会用 5 去初始化,因此 *p 的值为 5。

当然,在堆上分配内存的时候也可以不提供初始值,例如:
int *p = new int;
对于这种不提供显式初始化的情况,如果new的类型是类,则会调用该类的默认构造函数;如果是内部数据类型,则不会被初始化。

new 还可以申请一整块内存空间当做数组使用,其方法是在 new 的类型后面跟一个中括号,其中是数组的长度,语法格式如下:
new 类型[长度];
举个简单的例子:
int *arr = new int[5]; //定义长度为 5 的 arr 数组
自 C++11 标准开始,申请堆区数组空间的同时可以进行初始化,语法格式如下:
new 类型[长度]{值, 值, ...};
例如:
int *arr = new int[5]{1,2,3,4,5};
定义的 arr 数组中存放的元素分别是 1、2、3、4、5。

C++ delete

通过 new 分配的内存必须由开发者自己去释放,如果不手动释放,那么申请的堆内存就一直存在,直到程序执行结束才会被回收。

在 C++ 中使用 delete 来释放内存,语法格式为:
delete 指针名;

如果是当做数组使用的一整块对内存,delete 的语法格式为:
delete []指针名;
其中,中括号“[]”表示释放的是一个数组,这里不需要指定数组的长度。

例如:
int* p = new int;
int* arr = new int[5];

//释放先前申请的堆内存
delete p;
delete[]arr;

总结

在 C++ 中,new 分配的堆内存,必须用 delete 手动释放,否则会造成内存泄漏。

对于不是用 new 分配的内存空间,使用 delete 是不合法的。对于使用 new 分配的堆内存,只能 delete 一次,否则也会造成系统错误。

推荐阅读