allocator类
头文件:<memory>
作用:可以做到分配内存和对象构造分离。意味着分配的时候不用调用构造函数。可以在使用对象的时候再按需构造,省去了构造对象的时间,更加方便、高效。
相比之下,用new分配对象空间的时候,我们知道new会自动调用对象的构造函数,但是有时候我们在为对象开辟空间的时候,其实还没有准备好为对象赋值。
string *p = new string[n] //开辟空间并构造了n个空字符串对象
for(int i = 0; i < n; ++i)
{
cin>>p[i]; //为n个字符串对象赋值
}
在上例程序中,可以看到,用new来分配对象空间,调用构造函数为我们初始化了空字符串,这对我们来说并没有意义,因为我们知道后面通过赋值会覆盖原有的初始化值。
我们来看看allocator类将怎么处理:
allocator使用方法:
1.声明allocator类:
allocator<string> a;
2.分配未构造的空间:
string* p = a.allocate(n) //n个string 类型的原始空间,返回指向空间的指针
3.构造对象:
for(int i=0;i<n;i++)
{
string args;
cin>>args;
a.construct(p+i,args) //对每一个对象进行初始化,args是对应的构造函数参数,p必须是对应的allocate分配的指针
}
4.析构对象:
for(int i=0;i<n;i++)
a.destroy(p+i)
5.释放空间:
a.deallocate(p,n) //p是指针,n是对象个数
可以看到,通过适时的构造对象,我们无需通过赋值操作覆盖原对象的初始值,减少了不必要的操作。
注意事项:
- 使用allocator分配的内存,必须用construct构造对象才能使用,否则该行为未定义
- construct和destroy要一一对应,构造和析构配套
- deallocate的参数指针不得为空,且必须指向由allocator分配的内存;n对象个数必须与分配的对象个数一致(意味着我们不能分批释放资源)
placement new
placement new不是C++11才有的运算符。再此提及是因为和allocator功能较为相似
作用:用来在一个指定的内存地址上构造对象
用法:
1.提前分配空间
char *buf = new char[sizeof(string)]; // 在堆上分配空间,也可以在栈上分配空间:char buf[sizeof(string)];
2.使用operator new构造对象
string *p = new (buf) string("hi"); // placement new
3.析构对象
p->~string(); //显式调用析构函数
4.释放空间
delete []buf
placement new的使用较为自由,我们可以选择多种方式初始化原始空间,可以在栈上也可以在堆上分配空间。一般都是以char类型申请空间(因为char类型占1个字节)。需要注意的是:这里为char分配空间,使用了new,为什么我们还说是原始空间?因为基本数据类型没有构造对象的概念,它无需构造函数。
placement new使用的空间是可以反复利用的,在第三步析构对象之后,实际上我们可以使用buf指针指向的空间构造其他的对象
两者比较
总的来说,两者都能够达到分配内存和对象构造分离的目的,有一些区别如下:
- placement new是运算符,allocator是C++11提供的标准库类模板。
- placement new其实是承担了构造函数的任务,allocator是拥有分配空间、构造对象、析构对象、释放空间 一整套服务。
- placement new可以灵活构造不同的类型对象,allocator在一开始定义的时候必须提供一个确定的类型参数才能初始化allocator类,之后无法改变。
- placement new的使用场景:一般是为了在重点程序部分,减去分配空间的开销(使用new操作符分配内存需要在堆中查找足够大的剩余空间),并且提高部分代码的可靠性(不需要担心内存分配失败)
- allocator的使用场景:一般绝大多数情况可以被placement new代替。