定义变量使用的内存在程序运行前就确定了,有些时候我们希望能在运行期得到内存,可以使用操作符new和delete
new和new[]
new操作符可以分配动态内存,new后面需要跟着数据类型,如果需要多个该数据类型的元素,还需加上方括号,这个操作符返回的是新分配内存的头指针,语法是pointer = new type pointer = new type [number_of_elements]
第一个表达式用来分配一个type类型元素所需要的内存,第二句话用来分配一组元素,注意number_of_elements可以是变量,操作符的输入
int * foo;
foo = new int [5];
注意返回值是指针类型
注意声明正常数组和new数组有很大的不同,最大的区别就是正常数组的大小是常量表达式,编译期确定,new可以用任何变量作为大小(0或负数是否有意义)
动态内存分配在系统的堆上,但是计算器资源是有限的,因此不保证每次new都能返回想要的内存
C++提供了两个标准机制来确认分配是否成功
一个是通过异常,如果分配失败bad_alloc异常会被抛出,如果异常被抛出,然后程序没有对应的handler,程序会停止执行
异常throw可以认为是内建在new操作符里,作为默认行为```c++
foo = new int [5]; // if allocation fails, an exception is thrown
另外一种方式是使用nothrow,如果使用nothrow,则不会抛出异常,直接返回null指针,程序继续运行
实现上是传递一个特殊的对象
`foo = new (nothrow) int [5];`
这种情况需要自己检查指针是否为空
```c++
int * foo;
foo = new (nothrow) int [5];
if (foo == nullptr) {
// error assigning memory. Take measures.
}
nothrow可能会被异常效率更低,因为有显式地check代码,每次new后都会运行这堆代码,因此更推荐使用异常机制,至少对重要的分配应该这样处理,不过下面的例子主要使用nothrow,因为比较简洁
delete和delete[]
对于大部分情况,动态分配的内存是有明显的生命周期,一旦不需要这些内存,可以把这些内存归还系统,以便后续代码使用,delete的作用就是如此
delete pointer;
delete[] pointer;
delete delete[]和new new[]是配套使用的(操作系统或者c++语言会记录一些信息)
delete的输入要么是之前new内配的指针,要么是null指针(此时不会发生任何事)
// rememb-o-matic
#include <iostream>
#include <new>
using namespace std;
int main ()
{
int i,n;
int * p;
cout << "How many numbers would you like to type? ";
cin >> i;
p= new (nothrow) int[i];
if (p == nullptr)
cout << "Error: memory could not be allocated";
else
{
for (n=0; n<i; n++)
{
cout << "Enter number: ";
cin >> p[n];
}
cout << "You have entered: ";
for (n=0; n<i; n++)
cout << p[n] << ", ";
delete[] p;
}
return 0;
}
注意new的大小是一个变量
很有可能i输入太大,导致超过系统能分配的内存大小,此时程序会打印分配失败相关语句
通常认为程序能够处理分配失败是一个好习惯
C语言的动态内存
C语言不能使用new和delete,new和delete是操作符,内置于语言,C使用的是库函数malloc和free,是系统调用,机制是不一样的
注意new和malloc不要混用,支持的操作是不一样的
总结
程序员的基本修养:及时释放内存,不要造成内存泄漏,这样会影响程序的可持续性,运行足够长时间后没办法继续运行
delete和delete[]本质上的差别在对class类型进行释放的时候,调用析构函数的区别,那堆内存都会被释放,但是不调用析构函数的话,持有的其他资源没办法释放,比如new的内存、文件、端口号