new与allocator类
new和delete运算符一次分配/释放一个对象,但某些应用需要一次为很多对象分配内存的功能,例如vector、string都是在连续内存中保存它们的元素,因此当容器需要重新分配内存时,必须一次性为很多元素分配内存,为了支持这种需求C++语言和标准库提供了俩种一次分配一个对象数组的方法。
C++语言:new表达式(分配并初始化一个对象数组)
标准库:包含一个名为allocator的类(允许我们将分配和初始化分离,使用allocator通常会提供更好的性能和更灵活的内存管理能力)
当一个引用需要可变数量的对象时,应该使用标准库容器而不是动态分配的数组,使用容器更为简单、更不容易出现内存管理错误并且可能有更好的性能。使用容器的类可以使用默认版本的拷贝、赋值和析构操作,分配动态数组的类则必须定义自己版本的操作,在拷贝、赋值以及销毁对象时管理所关联的内存。
allocator
标准库allocator类定义在头文件memory中,它帮助我们将内存分配和对象构造分离开来,它提供一种类型感知的内存分配方法,它分配的内存是原始的、未构造的。类似vector,allocator是一个模版,为了定义一个allocator对象,我们必须指明这个allocator可以分配的对象类型,当allocator对象分配内存时,它会根据给定的对象类型来确定恰当的内存大小和对齐方式。
allocator分配的内存是未构造的,allocate函数返回的是原始分配且未构造的内存的首地址,我们需要在此内存中构造对象,新标准库中,construct成员函数接受一个指针和零个或多个额外参数,在给定位置构造一个元素,额外参数用来初始化构造的对象,这些额外参数必须是与构造的对象的类型相匹配的合法的初始化器。
allocator<string> alloc;
auto const p = alloc.allocate(10); //分配10个未初始化的string,p指向原始内存首地址
auto q = p;
alloc.construct(q++);
alloc.construct(q++, 10, 'c');
alloc.construct(q++, "hi");
//q指向最后构造的元素之后的位置
//cout << *q << endl; //灾难:q指向未构造的内存
cout << *(--q) << endl; //打印hi
cout << *(--q) << endl; //打印cccccccccc
cout << *(--q) << endl; //打印空
cout << p[0] << endl; //打印空
cout << p[1] << endl; //打印cccccccccc
cout << p[2] << endl; //打印hi
当我们用完对象后,必须对每个构造的元素调用destroy来销毁它们,函数destroy接受一个指针,对指向的对象执行析构函数。我们只能对真正构造了的元素进行destroy操作。一旦元素被销毁后,就可以重新使用这部分内存来保存其他string,也可以将其归还给系统,释放内存通过调用deallocate来完成。我们传递给deallocate的指针不能为空,它必须指向由allocate分配的内存,而且,传递给deallocate的大小参数必须与调用allocated分配内存时提供的大小参数具有一样的值。
while (q != p)
alloc.destroy(--q); //释放真正构造的string
alloc.deallocate(p,10);
拷贝和填充未初始化内存的算法
标准库还为allocator类定义了俩个伴随算法,可以在未初始化内存中创建对象。
vector<int> v{ 1,2,3,4,5 };
allocator<int> alloc;
//分配比v中元素所占用空间大一倍的动态内存
auto p = alloc.allocate(v.size() * 2);
//通过拷贝v中的元素来构造从p开始的元素
auto q = uninitialized_copy(v.begin(), v.end(), p);
//将剩余元素初始化为42
uninitialized_fill_n(q, v.size(), 42);