原文链接 http://blog.csdn.net/Xiejingfa/article/details/50955295

今天我们来讲讲C++的allocator类。


C++提供了new和delete操作符来管理动态内存空间。new操作通常需要完成两部分工作:一是在系统中申请内存空间,二是在分配的内存上构造对象。delete操作也通常需要完成对应的两部分工作:一个调用相应的析构函数销毁对象,二是回收内存。从这点看,new和delete操作符把内存空间的分配回收与对象的构建销毁紧紧关联在一起。对于new和delete的这种特性,如果我们只是申请单个对象的时候倒是很合适。因为我们几乎可以确定单个对象一定会被使用同时我们也希望将单个对象的内存和初始化组合在一起。

那如果我们需要分配一大块内存呢?这种情形就有点不同。考虑下面这段代码:

#include <iostream>
using namespace std;class Example
{
public:Example() { cout << "example default constructor..." << endl; }Example(int x) : a(x) { cout << "example constructor..." << endl; }~Example() { cout << "example destructor..." << endl; }int a;
};int main()
{// 如果Example没有默认构造函数,则无法动态分配数组Example *p = new Example[10];delete[] p;
}

在上面这段代码中,我们new了一个长度为10的Example数组,这时new操作符不仅申请了相应大小的内存空间,还在分别执行了每个元素的默认构造函数。所以它的输出如下:

example default constructor...
example default constructor...
example default constructor...
example default constructor...
example default constructor...
example default constructor...
example default constructor...
example default constructor...
example default constructor...
example default constructor...
example destructor...
example destructor...
example destructor...
example destructor...
example destructor...
example destructor...
example destructor...
example destructor...
example destructor...
example destructor...

如果我们new了长度为10的数组,但并没有全部使用呢?这样剩下那些没有使用的元素就这就产生了额外的对象构造的成本。可见,将内存分配和对象构造结合在一起可能会导致不必要的浪费。而且如果Example中没有默认的构造函数,我们也无法new出动态数组。

有没有一种方法可以将内存的分配回收和对象的构造销毁分离开,让我们在一块内存空间中按需分配对象呢?这就是我们今天要介绍的allocator类。

allocator类是一个模板类,定义在头文件memory中,用于内存的分配、释放、管理,它帮助我们将内存分配和对象构造分离开来。具体地说,allocator类将内存的分配和对象的构造解耦,分别用allocate和construct两个函数完成,同样将内存的释放和对象的析构销毁解耦,分别用deallocate和destroy函数完成。下面我们就来介绍一下这几种函数。

1、内存分配和对象构造

我们可以使用下面语句定义一个名为alloc为类型T分配内存的allocator对象:

allocator<T> alloc;

有了allocator对象后,我们可以使用下面的函数让系统为我们分配一段原始的、未构造的、可以保持n个类型为T的对象的内存空间:

alloc.allocate(n)

与new操作类似,allocate函数调用成功后返回一个指向该段内存第一个元素的指针。allocator分配的对象是未构造的,我们在使用前需要调用construct函数在此内存中构造对象,如果使用为构造的内存,其行为是未定义的。construct的使用方法如下:

alloc.construct(p, args)

p必须是一个类型为T*的指针,指向一块由allocator分配的未构造内存空间。arg为类型T构造函数的参数,用来在p指向的内存空间中构造一个T类型对象。

示例:

#include <iostream>
#include <memory>
using namespace std;class Example
{
public:Example() : a(0) { cout << "example default constructor..." << endl; }Example(int x) : a(x) { cout << "example constructor..." << endl; }~Example() { cout << "example destructor..." << endl; }int a;
};int main()
{allocator<Example> alloc;Example *p = alloc.allocate(2);alloc.construct(p);alloc.construct(p + 1, 3);cout << p->a << endl;p++;cout << p->a << endl;
}

在上面这段代码中,我们先申请了可以保存两个Example对象的内存空间,然后在第一个位置上调用Example的默认构造函数来构造对象,在第二个位置调用另一个构造函数来构造对象。输出如下:

example default constructor...
example constructor...
0
3

从输出中我们看到,我们只是完成了内存分配和对象构造的工作,构造好的对象并没有析构,内存也没有回收。下面我们来看看如何使用allocator类来完成对象析构和内存回收工作。

2、对象析构和内存回收

当我们使用完对象后,需要对每个构造的对象调用destroy函数来销毁它们。

alloc.destroy(p)

destroy函数接受一个T *的指针,对其指向对象执行构造函数。对象被析构销毁后,分配好的内存空间依然存在,我们可以重新在这块内存上继续构造对象,重复利用,也可以对该内存进行回收操作,归还给系统。内存释放用deallocate函数完成:

alloc.deallocate(p, n)

deallocate函数释放从p开始的长度为n的内存空间,其中p是allocate的返回值,n是该段内存 保存的元素个数,应和allocate的参数n保持一致。需要注意的是,必须由用户来保证调用deallocate前对每个在这块内存中创建的对象调用destroy函数。

示例:

class Example
{
public:Example() : a(0) { cout << "example default constructor..." << endl; }Example(int x) : a(x) { cout << "example constructor..." << endl; }~Example() { cout << "example destructor..." << endl; }int a;
};int main()
{allocator<Example> alloc;Example *p = alloc.allocate(2);alloc.construct(p);alloc.construct(p + 1, 3);cout << p->a << endl;cout << (p + 1)->a << endl;alloc.destroy(p);alloc.destroy(p + 1);alloc.deallocate(p, 2);}

输出如下:

example default constructor...
example constructor...
0
3
example destructor...
example destructor...

总结:

  1. allocator类将内存分配回收和对象构造析构分离开来,可以让我们先分配内存再按需构造。
  2. allocator类分配的内存是未构造的,为了使用已经分配好的内存,我们必须使用construct构造对象。如果使用未构造的内存,其行为是未定义的。
  3. 只能对真正构造了的对象进行destroy操作,用户必须保证在调用deallocate函数回收内存前对这块内存上的每个元素调用destroy函数。

下面自己试试,做几个例子:

#include <string>
#include <iostream>
#include <memory>
using namespace std;
int main()
{
allocator<string> alloc;
int n = 100;
//分配n个元素类型为string的空间,allocate返回指向首元素的地址
//allocate分配的对象是没有构造的,我们需要单独去构造对象
string * p = alloc.allocate(100);
//下面来构造对象
string *q1 = p;
//每个对象都初始化为"abcde"
while(q1 != p + n){//调用construct:格式是 a.construct(p,argc),argc是//参数alloc.construct(q1 ++,"abcde");}
string str;
string *q = p;
while(q != p + 100){cout << *q ++ << endl;}q = p;
q1 = p;while(q != p + 100)
{
//调用 a.destroy(p),p是指向构造的元素的一个指针,此算法对p指向的元素执行
//析构函数alloc.destroy(q ++);
}//调用a.deallocate(p,n)算法释放由于allocate分配的空间,其中p必须是allocate返回的//指针
alloc.deallocate(q1,100);
return 0;
}

运行结果是:

abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde
abcde

总结,从上面可以看出,如果要使用allocator动态分配内存,特点是内存分配和对象的构造分离,内存的释放和对象的析构分离。

首先,假设现在分配内存要存储的元素类型是T,那么

第一步:先调用 allocator<T>  alloc,得到一个alloc,可以用它分配和释放空间,构造和析构对象。

第二步:分配未构造的空间,如下代码:

string *p = alloc.allocate(n);

其中调用allocate分配空间,n是分配n未构造的原始的空间。返回类型是T*

第三步:在刚才分配的空间上构造元素,方法是调用a.construct(p,args),其中a是allocator对象,p是第二步分配的未构造的原始的空间,argc是被传递给类型为T的构造函数。例如:

//n是一个类似整形的一个数
alloc.construct(p,n);

第四步:调用a.destroy析构allocator对象中的每个元素,格式是a.destroy(p),此算法对p指向的对象执行元素的析构函数。例如:

q = p;
while(q != p + n)alloc.destroy(q ++);

第五步:调用a.deallocate(p,n)来释放allocate分配的空间,一次性释放。p是allocate分配后得到的指向第一个对象的指针,allocate分配的存放n个元素的空间。例如:

alloc.deallocate(p,n);

指向完上述过程就是allocator类就的完整的使用过程。


【C++11新特性】 - 空间配置allocator类相关推荐

  1. C++11新特性之新类型与初始化

    C++11新特性之新类型与初始化 snoone | 2016-06-23 11:57    浏览量(148)    评论(0)   推荐(0) 数据 这是C++11新特性介绍的第一部分,比较简单易懂, ...

  2. C++11新特性以及std::thread多线程编程

    一 .C++11新特性 1. auto 类型推导 1.1 当=号右边的表达式是一个引用类型时,auto会把引用抛弃,直接推导出原始类型: 1.2 当=号右边的表达式带有const属性时,auto不会使 ...

  3. 【C/C++】C++98基础上的C++11新特性

    一.新语法 1.自动类型推导auto auto的自动推导,用于从初始化表达式中推断出变量的数据类型. //C++98 int a = 10; string s = "abc"; f ...

  4. 【C++面试宝典】C++11新特性知识总结

    目录 C++11新特性 auto自动类型推导 decltype类型指示符 nullptr常量 lambda表达式(匿名函数) 智能指针 auto_ptr unique_ptr shared_ptr w ...

  5. C++11新特性(原封不动转载待查)

    C++11标准发布已有一段时间了, 维基百科上有对C++11新标准的变化和C++11新特性介绍的文章. 我是一名C++程序员,非常想了解一下C++11. 英文版的维基百科看起来非常费劲,而中文版维基百 ...

  6. COSMIC的后端学习之路——2.1 C++11新特性(1)

    2.1 C++11新特性(1) 知识树 1.智能指针 (1)std::shared_ptr:共享的智能指针 ①初始化 ②获取原始指针 ③指定删除器(自定义删除对象) ④一些错误用法 (2)std::u ...

  7. 【C++】C++11新特性列表

    我们学习的标准是C++98,我们知道计算机的知识更新非常快,本文旨在大致了解C++11的新特性,如果想要仔细了解,请阅读<C++Primer中文版 第五版>本文的页码也是这本书的页码,这里 ...

  8. C++11新特性之nullptr

    在C++11之前的C++98/03我们使用空都是NULL关键字,后来C++11之后新增了nullptr关键字来表示空.那么有了NULL还要弄个nullptr出来干什么呢?是不是吃太饱了? 为了来剖析这 ...

  9. C++11 新特性之std::thread

    C++11 新特性之std::thread 原文:https://blog.csdn.net/oyoung_2012/article/details/78958274 从C++11开始,C++标准库已 ...

最新文章

  1. IDEA的常用操作(快捷键)
  2. SVG配电站接线系统绘制
  3. 《JAVA练习题目9》 创建一个Person类和一个Family类。(这次的OJ实在写的没有状态,被样例搞崩了,这次代码还有好多代码复制等一系列不良现象,就是为了过OJ写的。。大家凑活看一下吧)
  4. 如何阅读苹果开发文档
  5. 论文笔记:[ICLR 2020] Tips for prospective and early-stage PhD students
  6. 深度学习笔记第一门课第一周:深度学习引言
  7. pandas后台导出excel_pandas导出Excel表格,银行卡号、身份证号无法正常显示的问题,该怎么解决?...
  8. 7.18自学c++笔记
  9. 经典vim插件功能说明、安装方法和使用方法
  10. eeglab加载显示脑电数据,eeglab简单操作
  11. ico图标生成器系统 断网情况下快速生成ico文件
  12. 学习笔记-DQPSK系统的调制与解调
  13. 山西省忻州市水泥厂能耗监测系统的设计与应用
  14. 【论文阅读一】Adaptive Cross-Modal Few-shot Learning
  15. 计算机装系统找不到硬盘,重装win10系统找不到硬盘完美解决方法
  16. Excel VBA编程常用语句300句
  17. 全国计算机等级三级网络技术试卷详解(二)
  18. 通过开源工具XCA工具签发和管理可被浏览器信任的SSL证书
  19. java模拟点击_java 模拟用户点击事件
  20. MYSQL安装和初始化配置

热门文章

  1. Java:多线程,CyclicBarrier同步器
  2. 诗歌rails 之with_options
  3. java io内存泄露_java内存泄露和OutOfMemory
  4. [网络安全提高篇] 一一〇.强网杯CTF的Web Write-Up(上) 寻宝、赌徒、EasyWeb、pop_master
  5. [网络安全自学篇] 四十.phpMyAdmin 4.8.1后台文件包含缺陷复现及防御措施(CVE-2018-12613)
  6. 【数据结构与算法】之深入解析“H指数”的求解思路与算法示例
  7. HarmonyOS之深入解析图像的位图操作和属性解码
  8. iOS之深入探究CADisplayLink和NSTimer的对比和内存溢出问题
  9. iOS之“微信支付”开发流程
  10. 【IT资讯】年薪170万码农征友,条件让网友炸锅……