allocator类编程实验
本文是本人大一期间在校学习C++课程时所撰写的实验报告的摘录,由于刚上大学,刚接触计算机编程方面的相关知识,故可能会有很多不足甚至错误的地方,还请各位看官指出及纠正。
本文所涉及到的“教材”指:
电子工业出版社《C++ Primary中文版(第5版)》
如需转载或引用请标明出处。
基本知识
allocator
当分配一大块内存时,我们通常计划在这块内存上按需构造对象。在此情况下,我们只在真正需要时才执行对象的构建操作。
标准库类型allocator可以帮助我们将内存分配和对象构造分离开来,它分配的内存是原始的、未构造的,它定义在头文件memory中。下面是allocator的一些相关操作:
allocator<T> a //定义一个名为a的allocator对象,可以为类型为T的对象分配内存
a.allocate (n) //分配一段原始的、未构造的内存,保存n个类型为T的对象
a.destroy (p) //p为T*类型的指针, 对p指向的对象执行相应的析构函数
a.deallocate (p, n) //释放从T*指针p中地址开始的内存,其中n必须是p创建时的大小。在调用该函数前,必须对该内存中的每个对象调用destroy
a.construct (p, args) //T*指针指向一块原始内存,arg被传递给类型为T的构造函数,在p所指内存中构造对象
定义一个allocator时必须指明其可以分配的对象类型。当一个allocator对象分配内存时,它会根据给定的对象类型来确定恰当的内存大小和对其位置:
allocator<string> alloc; //alloc可以分配string
string *p = alloc.allocate(n); //分配n个未初始化的string
allocator分配的内存是未构造的,我们按需在此内存中构造对象。construct成员函数接受一个指针和零个或多个额外参数,在给定位置构造一个元素。额外参数用来初始化构造的对象。例如:
alloc.construct(q++); //*q为空字符串
alloc.construct(q++, 3, 'c'); //*q为ccc
alloc.construct(q++, "hi") //*q为hi
在我们用完对象后,必须对每个构造的对象调用destroy来销毁它们。一旦元素被销毁后,就可以重新使用这部分内存来保存其他string,也可以将其归还给系统。释放内存通过调用deallocate来完成。其中的指针参数必须指向allocate分配的内存,而且传递给deallocate的大小参数也必须和调用allocated分配内存时提供的大小参数具有一样的值。
uninitialized_copy、uninitialized_fill等函数
标准库还为allocator类定义了两个伴随算法,可以在未初始化内存中创建对象,这些函数定义在memory头文件中,描述如下:
uninitialized_copy (b, e, b2) //从迭代器b和e指定的范围中拷贝元素到迭代器b2指定的未构造的原始内存中
uninitialized_copy_n (b, n, b2) //从迭代器b指定的位置开始,拷贝n个元素到b2开始的内存中
uninitialized_fill (b, e, t) //从迭代器b和e指定的原始内存范围中创建对象,对象的值均为t的拷贝
uninitialized_fill_n (b, n, t) //从迭代器b指定的位置开始创建n个对象,对象的值均为t的拷贝
uninitialized_copy接受三个迭代器参数。前两个表示输入序列,第三个表示这些元素要拷贝到的目的空间。传递给uninitialized_copy的目的位置迭代器必须指向未构造的内存。返回递增后的目的位置迭代器,因此,一次uninitialized_copy调用会返回一个指针,指向最后构造的元素之后的位置。
uninitialized_fill则更像是填充函数。
这些函数的目标地址都必须足够大,能容纳要创建或拷贝的对象。
示例程序
usealloc.cpp主要展示了allocator的相关用法及使用数组和allocator的区别,usealloc2.cpp主要展示了使用uninitialized_copy和uninitialized_fill使用方法。
示例代码
usealloc.cpp
#include <string>
using std::string;
#include <memory> //使用allocator
using std::allocator;
#include <cstddef> //使用size_t
using std::size_t;
#include <iostream>
using std::cout; using std::endl;
#include <fstream> //使用文件流
using std::ifstream;
int main()
{
const size_t n = 100;
allocator<string> alloc; //alloc是一个可以分配string的对象
string *p = alloc.allocate(n); //p指向alloc分配的n个没有初始化的的string的内存空间的开始
string *q = p; //q指向最后构造的元素之后的位置
alloc.construct(q++, string()); //在q指向的位置(即p)处,初始化一个空string,再递增q的位置
cout << *(q - 1) << endl; //输出上面的string
alloc.construct(q++, string(10, 'c')); //初始化下一个string,由十个‘c’组成
cout << *(q - 1) << endl; //输出上面的string
alloc.construct(q++, string("hi")); //初始化下一个string,是“hi”
cout << *(q - 1) << endl; //输出上面的string
cout << *p << endl; //因为p指向第一个字符串的位置,所以可以解引用输出
while (q != p) //析构上述初始化的string
alloc.destroy(--q); //因为q指向尾后,所以先递减q,再对其指向的string执行对应的析构函数
alloc.deallocate(p, n); //回收之前分配的空间
p = alloc.allocate(n); //重新分配n个没有初始化的的string的内存空间
string s; //s用于临时存储读入
q = p; //q指向上面分配空间的第一个string
ifstream in("data/storyDataFile"); //声明一个文件输入流in,读取目录下data/storyDataFile的内容
while (in >> s && q != p + n)
alloc.construct(q++, s); //当有读入一个string时才初始化一个string
size_t size = q - p; //指示有多少个string初始化
cout << "read " << size << " strings" << endl;
for (q = p + size - 1; q != p; --q)
alloc.destroy(q); //析构每一个已初始化的string
alloc.deallocate(p, n); //回收之前分配的空间
//使用数组代替上面的实现
in.close(); //关闭文件输入流in
in.open("data/storyDataFile"); //重新打开文件输入流in
p = new string[n]; //初始化n个string,构成数组,p指向第一个的位置
q = p;
while (in >> s && q != p + n)
*q++ = s; //对q指向的string重新赋值
size = q - p; //指示有多少个string被重新赋值
cout << "read " << size << " strings" << endl;
delete[] p; //用delete[]释放数组
system("pause");
return 0;
}
运行结果:
创建data/storyDataFile文件:
运行程序,结果如下:
说明in文件读入流以任意空白作为是否读取一个string的判断依据。
usealloc2.cpp
#include <cstddef>
using std::size_t;
#include <string>
using std::string;
#include <vector>
using std::vector;
#include <memory> //使用allocator、uninitialized_copy等
using std::uninitialized_copy;
using std::allocator; using std::uninitialized_fill_n;
#include <iostream>
using std::cout; using std::endl;
int main()
{
int temp[] = {1,2,3,4,5,6,7,8,9};
vector<int> vi(temp, temp + sizeof(temp)/sizeof(*temp));
allocator<int> alloc;
int *p = alloc.allocate(vi.size() * 2); //alloc所占有的空间是vi的两倍,其被p指向
int *q = uninitialized_copy(vi.begin(), vi.end(), p); //将vi中的元素都拷贝到alloc中,q指向尾后元素
uninitialized_fill_n(q, vi.size(), 42); //将剩下的空间都初始化为42
for (size_t i = 0; i != vi.size(); ++i) //输出前一半元素
cout << *(p + i) << " ";
cout << endl;
for (size_t i = 0; i != vi.size(); ++i) //输出后一半元素
cout << *(q + i) << " ";
cout << endl;
alloc.deallocate(p, vi.size() * 2);
system("pause");
return 0;
}
运行结果: