文章目录
某些应用需要一次性为很多对象分配内存的功能。C++中提供了两种一次分配一个对象数组的功能:new
、allocator
,其中后者允许将分配和初始化分离,提供更好的性能和更灵活的内存管理能力。
大多数应用应该使用标准库容器而不是动态分配的数组。使用容器更为简单,更不容易出现内存管理错误并且可能拥有更好的性能。
使用容器的类可以使用默认的拷贝、赋值和析构操作。分配动态数组的类必须定义自己版本的操作,在拷贝、复制以及摧毁对象时管理所管理的内存。
new和数组
cpp
//get_size()即使为0,也是合法的。但是不能解引用,类似尾后指针
int *pia = new int[get_size()];
typedef int arrT[42];
int *p = new arrT; //分配一个42个int的数组,p指向第一个int
虽然我们通常称new T[]
分配的内存为动态数组,但这种叫法有些误导。我们得到的并非要给数组类型的对象,而是得到一个数组元素类型的指针(没有数组的维度)。因此不能对动态数组调用begin和end,也不能使用范围for语句来处理动态数组中的元素。
初始化动态分配对象的数组
默认情况下,new分配的对象都是默认初始化的。可在后面跟一个()进行值初始化:
cpp
int *pia = new int[10]; //10个未定义
int *pia2 = new int[10](); //10个0
string *psa = new string[10]; //10个空string
string *psa2 = new string[10](); //10个空string
在新标准中,还可以使用初始化器{}
cpp
int *pia3 = new int[10]{0, 1, 2}; //剩余元素进行值初始化
string *psa3 = new string[10]{"a", "an", string(3, 'x')};
释放动态数组
cpp
delete p; //p为动态对象或空
delete [] pa; //pa为一个动态分配的数组或空,逆序摧毁数组中元素
智能指针和动态数组
- unique_ptr
cpp
//无需提供自定义删除器,类型为T[]
unique_ptr<int []> up(new int[10]);
up.release(); //自动使用delete []摧毁其指针
//unique_ptr可以使用下标运算符
for(size_t i = 0; i != 10; ++i)
up[i] = i;
- shared_ptr
cpp
//不直接支持管理动态数组,类型为T,必须提供删除器
shared_ptr<int> sp(new int[10], [](int *p){delete [] p;});
//不支持下表访问
for(size_t i = 0; i != 10; ++i)
*(sp.get() + i) = i;
allocator
new在灵活性上有一定的局限性。其将内存分配和对象构造组合在了一起。delete同理。当分配一块大内存时,通常计划按需构造对象,因此需要将内存分配和对象构造分离。
cpp
string *const p = new string[n];
string s;
string *q = p;
while(cin >> s && q != p + n)
*q++ = s;
const size_t = q - p;
delete [] p;
- new表达式分配并初始化了n个string。但是,我们可能并不需要n个string;
- 对于确实需要使用到的对象,也在初始化后立即赋予了新值。每个使用到的元素都被赋值了两次;
- 对于没有默认构造函数的类,不能使用动态数组。
allocator类
当一个allocator对象分配内存时,它会根据给定的对象类型来确定恰当的内存大小和对齐位置:
cpp
allocator<string> alloc; //可以分配string的对象
auto const p = alloc.allocate(n); //分配n个未初始化的string

初始化
cpp
auto p = q;
alloc.construct(q++); //string默认构造函数构建q,q++
alloc.construct(q++, 10, 'c'); //q指向"ccc..."
alloc.construct(q++, "hi"); //q指向"hi"
cout << *p << endl; //ok
cout << *q << endl; //err,当前q还未构造
摧毁对象
当我们使用完对象之后,必须对每个构造的元素调用destroy来摧毁他们。值得注意的是,只有真正构造了的元素才能使用该操作。
cpp
while (q != p)
alloc.destroy(--q);
归还内存
一但元素被摧毁之后,就可以重新使用这部分内存,也可以通过deallocate来归还内存:
cpp
alloc.deallocate(p, n);
传递给deallocate的指针不能为空,必须指向alloc分配的内存,n也必须和分配的一致。
拷贝和填充未初始化内存的算法

假定有一个int的vector,希望将其内容拷贝到动态内存中。我们可以分配一块必vector元素所占空间大一倍的动态内存,然后将袁vector的元素拷贝到前一半空间,多一半用一个值填充:
cpp
auto p = alloc.allocate(vi.size() * 2);
auto q = uninitialized_copy(vi.begin(), vi.end(), p); //返回最后一个构造元素之后的指针
uninitialized_fill_n(q, vi.size(), 42);