C++语言和标准库提供了两种一次分配一个对象数组的方法。C++语言定义了另一种new表达式语法,可以分配并初始化一个对象数组。标准库中包含一个名为allocator的类,允许我们将分配和初始化分离。使用allocator通常会提供更好的性能和更灵活的内存管理能力。
new和数组
为了让new分配一个对象数组,我们要在类型名之后跟一对方括号,在其中指明要分配的对象的数目。在下例中,new分配要求数量的对象并(假定分配成功后)返回指向第一个对象的指针:
1 | int get_size_new() |
在main函数中调用new_array会输出42个0,因为new 分配的数组初始值都为0。
为了释放动态数组,我们使用一种特殊形式的delete——在指针前加上一个空方括号对.
方括号中的大小必须是整型,但不必是常量。也可以用一个表示数组类型的类型别名,来分配一个数组,这样,new表达式中就不需要方括号了:
1 | void new_array() |
虽然我们通常称new T[]
分配的内存为“动态数组”,但这种叫法某种程度上有些误导。
当用new分配一个数组时,我们并未得到一个数组类型的对象,而是得到一个数组元素类型的指针。
即使我们使用类型别名定义了一个数组类型,new也不会分配一个数组类型的对象。new返回的是一个元素类型的指针。
由于分配的内存并不是一个数组类型,因此不能对动态数组调用begin或end。
要记住我们所说的动态数组并不是数组类型,这是很重要的。
可以通过{}初始化动态数组
1 | void new_array() |
如果{}初始化列表小于数组长度,则默认补充空值,int补充0,string补充空字符串。
动态分配一个大小为0的数组是合法的
1 | void new_array() |
当n为0时,开辟了一个长度为0的动态数组,因为循环条件p != n+p_array,所以不会进入循环。
当我们用new分配一个大小为0的数组时,new返回一个合法的非空指针。此指针保证与new返回的其他任何指针都不相同。
对于零长度的数组来说,此指针就像尾后指针一样,我们可以像使用尾后迭代器一样使用这个指针。
可以用此指针进行比较操作,就像上面循环代码中那样。可以向此指针加上(或从此指针减去)0,也可以从此指针减去自身从而得到0。但此指针不能解引用——毕竟它不指向任何元素。
智能指针和动态数组
标准库提供了一个可以管理new分配的数组的unique_ptr版本。为了用一个unique_ptr管理动态数组,我们必须在对象类型后面跟一对空方括号:
1 | void unique_array() |
类型说明符中的方括号<int[]>
指出up指向一个int数组而不是一个int。由于unarray指向一个数组,当unarray销毁它管理的指针时,会自动使用delete[]。
当一个unique_ptr指向一个数组时,我们可以使用下标运算符来访问数组中的元素:
1 | void unique_array() |
shared_ptr也可以管理动态数组,这一点在C++ primer 第5版里没有提及,但是我自己测试好用
1 | void shared_array() |
C++ primer 第5版推荐的用法如下
1 | void use_shared_array() |
上例中,shared_ptr管理一个动态数组并提供了删除器。
allocator类
当分配一大块内存时,我们通常计划在这块内存上按需构造对象。在此情况下,我们希望将内存分配和对象构造分离。这意味着我们可以分配大块内存,但只在真正需要时才真正执行对象创建操作(同时付出一定开销)。
类似vector,allocator是一个模板。为了定义一个allocator对象,我们必须指明这个allocator可以分配的对象类型。
当一个allocator对象分配内存时,它会根据给定的对象类型来确定恰当的内存大小和对齐位置:
1 | void use_allocator() |
上述代码用allocator构造alloc对象,说明开辟的空间是为string对象准备的,然后调用allocate开辟空间,但是这些空间不能直接使用,需要调用构造函数才能使用,我们用allocator类的construct来构造对象。
1 | void use_allocator() |
循环中通过construct为每个q指向的空间构造string对象,对象的内容就是str的内容,str会随着循环每次增加c,所以上面的代码输出如下
1 | c |
另外stl也提供了一些拷贝和填充内存的算法
1 | void use_allocator() |
通过uninitialized_copy将ivec元素拷贝到p指向的空间,同样完成了构造。
uninitialized_fill_n将剩余ivec大小未构造的空间全部初始化为42。
总结
本文介绍了动态数组开辟的方法,利用new关键字可以开辟动态数组,利用delete[]可以回收数组。
也实现了通过shared_ptr和unique_ptr等智能指针管理动态数组的方案。
最后通过列举allocator的一些方法,展示了如何实现开辟空间和构造对象分离的方式动态构造对象。
源码连接
https://gitee.com/secondtonone1/cpplearn
想系统学习更多C++知识,可点击下方链接。
C++基础