第十二章动态内存——new和数组

动态数组

new和delete运算符一次分配/释放一个对象,但是某些应用需要一次为很多对象分配内存的功能。例如string和vector都是在连续内存中保存它们的元素。当容器要重新分配内存时,必须要一次性为很多元素分配内存。

c++语言和标准库提供了两种一次分配一个对象数组的方法:

  • 另一种new表达式语法,可以分配并且初始化一个对象数组
  • 标准库包含一个名为allocator的类,允许我们将分配和初始化分离

Best Practices:大多数应用应该使用标准库容器而不是动态分配的数组。使用容器更为简单并且更不容易出现内存管理错误并且有更好的性能。 

使用容器的类可以使用默认版本的拷贝、赋值和析构操作。分配动态数组的类必须定义自己版本的操作,在拷贝、赋值以及销毁对象时所关联的内存。

new和数组

为了让new分配一个对象数组,我们要在类型名之后跟一堆方括号,在其中指明要分配的对象的数目。new分配要求数量的对象并且返回指向第一个对象的指针:

int *pia = new int[get_size()];//pia指向第一个int对象

方括号中大小必须是整形,但是不必要是常量。和定义静态数组不同!静态数组的定义大小必须是常量!

int a[5];//正确!
int a[N];//错误!

也可以用一个表示数组类型的类型别名来分配一个数组,这样new表达式中就不需要方括号了:

typedef int arrT[43];//arrT 表示42个int的数组类型
int *p = new arrT;

分配一个数组会得到一个元素类型的指针

通常称new T[ ]分配的内存为“动态数组”,但这种叫法某种程度是有误导的。当使用new分配一个数组时,并没有得到一个数组类型的对象,而是得到一个数组元素类型的指针。由于分配的内存并不是一个数组类型,因此不能对动态数组调用begin或end。这些函数使用数组维度来返回指向首元素和尾后元素的指针。

WARNING:要记住我们所说的动态数组并不是数组类型,而是一个指向数组元素的指针。

初始化动态分配对象的数组

默认初始化:如果定义变量时没有指定初值,则变量会被默认初始化。默认值到底是什么由变量类型决定。

默认情况下,new分配的对象不管是单个分配的还是数组中的,都是默认初始化。可以对数组中的元素进行值初始化方法是在大小之后跟一对空括号

int *pia = new int[10]; //默认初始化,内置类型的局部变量不初始化
int *pia = new int[10](); //10个值初始化为0的int

string *psa = new string[10]; //默认初始化,string类默认初始化为空string
string *psa2 = new string[10](); //值初始化,空string

还可以使用花括号列表初始化:

int *pia3 = new int[10]{0,1,2,3,...};

string *psa3 = new string[10]{"a","an"};//前两初始化器初始化了,后面的进行值初始化。

如果初始化器数目大于元素数目,则new表达式失败,不会分配任何内存。本例中new会抛出一个类型为bad_array_new_length的异常。类似bad_alloc,此类型定义在头文件new之中。

动态分配一个空数组是合法的——但是不能解引用

虽然我们不能创建一个大小为0的静态数组对象,但是当n == 0时候,调用new[n]是合法的:

char arr[0];    //错误:不能定义长度为0的数组
char *cp =new char[0];    //正确,但是cp不能解引用

但是此指针不能解引用——毕竟它不指向任何元素。

释放动态数组

为了释放动态数组(一个指向数组的指针)时,我们使用一种特殊形式delete——在指针前加上一个空方括号对

delete p;        //p必须指向一个动态分配的对象或为空
delete []pa;     //pa必须指向一个动态分配数组或者未空

数组中的元素按逆序销毁,最后一个元素首先被销毁,然后是倒数第二个。

WARNING:如果我们在delete一个指向数组的指针时忽略了方括号(或者在delete一个指向单一对象的指针使用了方括号),其行为是未定义的。编译器很可能不会给出警告,但是我们的程序可能在执行过程中在没有任何警告的情况下行为异常。

智能指针和动态数组

标准库提供了一个可以管理new分配的数组的unique_ptr。为了用一个unique_ptr管理动态数组,必须在对象类型后面跟一对空方括号:

//up指向一个包含10个初始化int的数组
unique_ptr<int[]> up(new int[10]);
up.release();//自动用delete[]销毁其指针

类型说明符中方括号(<int[ ]>)指出up指向一个int数组而不是一个int。由于up指向一个数组,当up销毁它管理的指针时,会自动使用delete[]。当一个unique_ptr指向一个数组时,我们不能使用点和箭头成员运算符;我们可以使用下表运算符来访问数组中的元素:

for(size_t i =0;i!=10;i++)
    up[i] = i;

与unique_ptr不用,shared_ptr不直接支持管理动态数组。如果希望使用shared_ptr管理一个动态数组,必须提供自己定义的删除器:

shared_ptr<int> sp(new int[10],[](int *p)){delete[] p});
sp.reset();//使用我们提供的lambda释放数组,它使用delete[]

如果未提供删除器,这段代码是未定义的。因为默认情况下,shared_ptr使用delete销毁它所指向的对象。如果此对象是一个动态数组,对其使用delete所产生的问题与释放一个动态数组指针时忘记[]产生的问题一样。

shared_ptr不支持支持动态数组管理这一特性会影响我们如何访问数组中的元素,shared_ptr未定义下表运算符,而且只能指针类型不支持指针算术运算。为了访问数组中的元素,必须用get或缺一个内置指针,然后用它来访问数组元素。

for(size_t i =0;i!=10;++i)
    *(sp.get()+i) = i;    //使用get获取一个内置指针

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值