分配器

组成C++标准库的重要部分

C++编程中,分配器(英语:allocator)是C++标准库的重要组成部分。C++的库中定义了多种被统称为“容器”的数据结构(如链表集合等),这些容器的共同特征之一,就是其大小可以在程序的运行时改变;为了实现这一点,进行动态内存分配就显得尤为必要,在此分配器就用于处理容器对内存的分配与释放请求。换句话说,分配器用于封装STL容器在内存管理上的低层细节。默认情况下,C++标准库使用其自带的通用分配器,但根据具体需要,程序员也可自行定制分配器以替代之。

简介
C++编程中,分配器(英语:allocator)是C++标准库的重要组成部分。C++的库中定义了多种被统称为“容器”的数据结构(如链表集合等),这些容器的共同特征之一,就是其大小可以在程序的运行时改变;为了实现这一点,进行动态内存分配就显得尤为必要,在此分配器就用于处理容器对内存的分配与释放请求。换句话说,分配器用于封装STL容器在内存管理上的低层细节。默认情况下,C++标准库使用其自带的通用分配器,但根据具体需要,程序员也可自行定制分配器以替代之。
分配器最早由亚历山大·斯特潘诺夫作为C++标准模板库(Standard Template Library,简称STL)的一部分发明,其初衷是创造一种能“使库更加灵活,并能独立于底层数据模型的方法”,并允许程序员在库中利用自定义的指针引用类型;但在将标准模板库纳入C++标准时,C++标准委员会意识到对数据模型的完全抽象化处理会带来不可接受的性能损耗,为作折中,标准中对分配器的限制变得更加严格,而有鉴于此,与斯特潘诺夫原先的设想相比,现有标准所描述的分配器可定制程度已大大受限。
虽然分配器的定制有所限制,但在许多情况下,仍需要用到自定义的分配器,而这一般是为封装对不同类型内存空间(如共享内存与已回收内存)的访问方式,或在使用内存池进行内存分配时提高性能而为。除此以外,从内存占用和运行时间的角度看,在频繁进行少量内存分配的程序中,若引入为之专门定制的分配器,也会获益良多。
背景
亚历山大·斯特潘诺夫与李梦(Meng Lee)在1994年将标准模板库草案提交给C++标准委员会。提交伊始,草案就得到了委员会的初步支持,但委员会成员也对此提出了一些意见,尤其是要求斯特潘诺夫定制库内的容器,使之与底层存储模型相独立。作为对要求的回应,斯特潘诺夫发明了分配器,而正因此,标准模板库的所有容器接口也被迫重写,以与分配器相兼容。在修改标准模板库以将之引入C++标准库的过程中,许多标准委员会成员(如安德鲁·克尼格与比雅尼·斯特劳斯特鲁普)也与斯特潘诺夫协同工作。他们亦发现自定义分配器甚至有应用于长生命周期(持续存储)的标准模板库容器的潜力,斯特潘诺夫对此的评论则是“重要而有趣的见解”。
在原有的提案里的分配器设定中,斯特潘诺夫杂糅了一些语言特性(如可将模板参数也定义为模板),但由于当时的编译器皆无法处理之,所以最终并未被标准委员会所接纳,斯特潘诺夫则如此描述当时的情形:“比雅尼·斯特劳斯特鲁普与安迪·克尼格需要花大量时间来检查我们是否正确使用了这些未实现的特性。”在分配器应用后,之前库中直接使用的指针引用类型也可以分配器所定义的类型替代,斯特潘诺夫亦曾如此描述分配器:“标准模板库有个不错的特性便是:唯一要提及机器相关类型的地方(……)(只需)被封装成(仅)约16行内的代码。”除此以外,斯特潘诺夫原本还打算在分配器中完全封装存储模型,但标准委员会意识到这一做法会造成无法接受的性能损失,因而为补偿之,分配器的使用需求也做了一定扩充。
分配器的应用中比较特别的一点是,容器的实现过程中可能会假定分配器对指针与相关整型类型定义与默认分配器所提供的等价,因而给定分配器类型的所有实例在比较时常会得出“相等”的结果,而这一效果实际上恰与设计分配器的初衷背道而驰,并使带状态分配器的可用性大大受限,斯特潘诺夫后来对此评论道:“(分配器)理论上说是不差的主意(……)但不幸的是在实践中无法发挥其功效。“他洞察到若要令分配器更加实用,就有必要针对核心语言的引用部分进行修改。
使用需求
任意满足分配器使用需求的C++类都可作分配器使用。具体来说,当一个类(在此设为类A)有为一个特定类型(在此设为类型T)的对象分配内存的能力时,该类就必须提供以下类型的定义:
如此才能以通用的方式声明对象与对该类对象的引用T。allocator提供这些指针或引用的类型定义的初衷,是隐蔽指针或引用的物理实现细节;因为在16位编程时代,远指针(far pointer)是与普通指针非常不同的,allocator可以定义一些结构来表示这些指针或引用,而容器类用户不需要了解其是如何实现的。
虽然按照标准,在库的实现过程中允许假定分配器(类)A的A::pointer(指针)与A::const_pointer(常量指针)即是对T*与T const*的简单的类型定义,但一般更鼓励支持通用分配器。
另外,设有对于为某一对象类型T所设定的分配器A,则A必须包含四项成员函数,分别为分配函数、解除分配函数、最大个数函数和地址函数:
除此以外,由于对象的构造/析构过程与分配/解除分配过程分别进行,因而分配器还需要成员函数A::construct(构造函数)与A::destroy(析构函数)以对对象进行构造与析构,且两者应等价于如下函数:
以上代码中使用了placementnew语法,且直接调用了析构函数。
分配器应是可复制构造的,任举一例,为T类对象而设的分配器可由另一为U类所设的分配器构造。若某分配器分配了一段存储空间,则这段存储空间只能由与该分配器等价的分配器解除分配。分配器还需要提供一个模板类成员类template struct A::rebind { typedef A other; };,以模板 (C++)参数化的方式,借之来针对不同的数据类型获取不同的分配器。例如,若给定某一为整型(int)而设的分配器IntAllocator,则可执行IntAllocator::rebind::other以获取对应长整型(long)的相关分配器。实际上,stl::list实际要分配的是包含了双向链表指针的node,而不是实际分配int类型,这是引入了rebind的初衷。
与分配器相关联的operator ==,仅当一个allocator分配的内存可以被另一个allocator释放时,上述相等比较算符返回真。operator!=的返回结果与之相反。
自定义分配器
定义自定义分配器的主要原因之一是提升性能。利用专用的自定义分配器可以提高程序的性能,又或提高内存使用效率,亦或两者兼而有之。默认分配器使用new操作符分配存储空间,而这常利用C语言堆分配函数(malloc())实现。由于堆分配函数常针对偶发的内存大量分配作优化,因此在为需要一次分配大量内存的容器(如向量双端队列)分配内存时,默认分配器一般效率良好。但是,对于关联容器与双向链表这类需要频繁分配少量内存的容器来说,若采用默认分配器分配内存,则通常效率很低。除此之外,基于malloc()的默认分配器还存在许多问题,诸如较差的引用局部性,以及可能造成内存碎片化。
有鉴于此,在这一情况下,人们常使用基于内存池的分配器来解决频繁少量分配问题。与默认的“按需分配”方式不同,在使用基于内存池的分配器时,程序会预先为之分配大块内存(即“内存池”),而后在需要分配内存时,自定义分配器只需向请求方返回一个指向池内内存的指针即可;而在对象析构时,并不需实际解除分配内存,而是延迟到内存池的生命周期完结时才真正解除分配。
在“自定义分配器”这一话题上,已有诸多C++专家与相关作者参与探讨,例如斯科特·梅耶斯的作品《Effective STL》与安德烈·亚历山德雷斯库的《Modern C++ Design》都有提及。梅耶斯洞察到,若要求针对某一类型T的分配器的所有实例都相等,则可移植的分配器的实例必须不包含状态。虽然C++标准鼓励库的实现者支持带状态的分配器,但梅耶斯称,相关段落是“(看似)美妙的观点”,但也几乎是空话,并称分配器的限制“过于严苛”。例如,STL的list允许splice方法,即一个list对象A的节点可以被直接移入另一个list对象B中,这就要求A的分配器申请到的内存,可被B的分配器释放掉,从而推导出A与B的分配器实例必须相等。梅耶斯的结论是,分配器最好定义为使用静态方法的类型。例如,根据C++标准,分配器必须提供一个实现了rebind方法的other类模板。
另外,在《C++程序设计语言》中,比雅尼·斯特劳斯特鲁普则认为“‘严格限制分配器,以免各对象信息不同’,这点显然问题不大”(大意),并指出大部分分配器并不需要状态,甚至没有状态情形下性能反倒更佳。他提出了三个自定义分配器的用例:内存池型的分配器、共享内存型分配器与垃圾回收型分配器,并展示了一个分配器的实现,此间利用了一个内部内存池,以快速分配/解除分配少量内存。但他也提到,如此优化可能已经在他所提供的样例分配器中实现。
自定义分配器的另一用途是调试内存相关错误。若要做到这一点,可以编写一个分配器,令之在分配时分配额外的内存,并借此存放调试信息。这类分配器不仅可以保证内存由同类分配器分配/解除分配内存,还可在一定程度上保护程序免受缓存溢出之害。
使用方法
当初始化标准容器时,若需使用自定分配器,则可将其写入模板参数,以代替默认的std::allocator,如下所示:
正如其他所有C++类模板般,在初始化同一标准库容器时,若使用了不同的分配器,则所生成容器的类型亦不同。譬如,若函数需一整型向量数组std::vector作为参数,则其只能接受由默认分配器生成的整型向量数组。
C++11
通过加入“作用域”分配器,C++11标准进一步强化了分配器接口,从而保证带有嵌套式内存分配特点的容器(如字符串向量数组等)所分配到的内存皆来自容器自身的分配器。
另外,C++11标准删除了“给定类型的分配器在比较时总是相等”的模棱两可的要求,使带状态分配器不仅实用性得到提升,而且可管理进程外的共享内存。现今分配器的作用多为让程序员可以控制容器的内存分配,而非适应基底硬件的地址模型。事实上,C++11标准删去了分配器“自适应地址模型”的功能,结果抹消了其设计初衷。
全国各地天气预报查询

上海市

  • 市辖区
  • 云南省

  • 临沧市
  • 云南省

  • 丽江市
  • 云南省

  • 保山市
  • 云南省

  • 大理白族自治州
  • 云南省

  • 德宏傣族景颇族自治州
  • 云南省

  • 怒江傈僳族自治州
  • 云南省

  • 文山壮族苗族自治州
  • 云南省

  • 昆明市
  • 云南省

  • 昭通市
  • 云南省

  • 普洱市
  • 云南省

  • 曲靖市
  • 云南省

  • 楚雄彝族自治州
  • 云南省

  • 玉溪市
  • 云南省

  • 红河哈尼族彝族自治州
  • 云南省

  • 西双版纳傣族自治州
  • 云南省

  • 迪庆藏族自治州
  • 内蒙古自治区

  • 乌兰察布市
  • 内蒙古自治区

  • 乌海市
  • 内蒙古自治区

  • 兴安盟
  • 内蒙古自治区

  • 包头市
  • 内蒙古自治区

  • 呼伦贝尔市
  • 内蒙古自治区

  • 呼和浩特市
  • 内蒙古自治区

  • 巴彦淖尔市
  • 内蒙古自治区

  • 赤峰市
  • 内蒙古自治区

  • 通辽市
  • 内蒙古自治区

  • 鄂尔多斯市
  • 内蒙古自治区

  • 锡林郭勒盟
  • 内蒙古自治区

  • 阿拉善盟
  • 北京市

  • 市辖区
  • 吉林省

  • 吉林市
  • 吉林省

  • 四平市
  • 吉林省

  • 延边朝鲜族自治州
  • 吉林省

  • 松原市
  • 吉林省

  • 白城市
  • 吉林省

  • 白山市
  • 吉林省

  • 辽源市
  • 吉林省

  • 通化市
  • 吉林省

  • 长春市
  • 四川省

  • 乐山市
  • 四川省

  • 内江市
  • 四川省

  • 凉山彝族自治州
  • 四川省

  • 南充市
  • 四川省

  • 宜宾市
  • 四川省

  • 巴中市
  • 四川省

  • 广元市
  • 四川省

  • 广安市
  • 四川省

  • 德阳市
  • 四川省

  • 成都市
  • 四川省

  • 攀枝花市
  • 四川省

  • 泸州市
  • 四川省

  • 甘孜藏族自治州
  • 四川省

  • 眉山市
  • 四川省

  • 绵阳市
  • 四川省

  • 自贡市
  • 四川省

  • 资阳市
  • 四川省

  • 达州市
  • 四川省

  • 遂宁市
  • 四川省

  • 阿坝藏族羌族自治州
  • 四川省

  • 雅安市
  • 天津市

  • 市辖区
  • 宁夏回族自治区

  • 中卫市
  • 宁夏回族自治区

  • 吴忠市
  • 宁夏回族自治区

  • 固原市
  • 宁夏回族自治区

  • 石嘴山市
  • 宁夏回族自治区

  • 银川市
  • 安徽省

  • 亳州市
  • 安徽省

  • 六安市
  • 安徽省

  • 合肥市
  • 安徽省

  • 安庆市
  • 安徽省

  • 宣城市
  • 安徽省

  • 宿州市
  • 安徽省

  • 池州市
  • 安徽省

  • 淮北市
  • 安徽省

  • 淮南市
  • 安徽省

  • 滁州市
  • 安徽省

  • 芜湖市
  • 安徽省

  • 蚌埠市
  • 安徽省

  • 铜陵市
  • 安徽省

  • 阜阳市
  • 安徽省

  • 马鞍山市
  • 安徽省

  • 黄山市
  • 山东省

  • 东营市
  • 山东省

  • 临沂市
  • 山东省

  • 威海市
  • 山东省

  • 德州市
  • 山东省

  • 日照市
  • 山东省

  • 枣庄市
  • 山东省

  • 泰安市
  • 山东省

  • 济南市
  • 山东省

  • 济宁市
  • 山东省

  • 淄博市
  • 山东省

  • 滨州市
  • 山东省

  • 潍坊市
  • 山东省

  • 烟台市
  • 山东省

  • 聊城市
  • 山东省

  • 菏泽市
  • 山东省

  • 青岛市
  • 山西省

  • 临汾市
  • 山西省

  • 吕梁市
  • 山西省

  • 大同市
  • 山西省

  • 太原市
  • 山西省

  • 忻州市
  • 山西省

  • 晋中市
  • 山西省

  • 晋城市
  • 山西省

  • 朔州市
  • 山西省

  • 运城市
  • 山西省

  • 长治市
  • 山西省

  • 阳泉市
  • 广东省

  • 东莞市
  • 广东省

  • 中山市
  • 广东省

  • 云浮市
  • 广东省

  • 佛山市
  • 广东省

  • 广州市
  • 广东省

  • 惠州市
  • 广东省

  • 揭阳市
  • 广东省

  • 梅州市
  • 广东省

  • 汕头市
  • 广东省

  • 汕尾市
  • 广东省

  • 江门市
  • 广东省

  • 河源市
  • 广东省

  • 深圳市
  • 广东省

  • 清远市
  • 广东省

  • 湛江市
  • 广东省

  • 潮州市
  • 广东省

  • 珠海市
  • 广东省

  • 肇庆市
  • 广东省

  • 茂名市
  • 广东省

  • 阳江市
  • 广东省

  • 韶关市
  • 广西壮族自治区

  • 北海市
  • 广西壮族自治区

  • 南宁市
  • 广西壮族自治区

  • 崇左市
  • 广西壮族自治区

  • 来宾市
  • 广西壮族自治区

  • 柳州市
  • 广西壮族自治区

  • 桂林市
  • 广西壮族自治区

  • 梧州市
  • 广西壮族自治区

  • 河池市
  • 广西壮族自治区

  • 玉林市
  • 广西壮族自治区

  • 百色市
  • 广西壮族自治区

  • 贵港市
  • 广西壮族自治区

  • 贺州市
  • 广西壮族自治区

  • 钦州市
  • 广西壮族自治区

  • 防城港市
  • 新疆维吾尔自治区

  • 乌鲁木齐市
  • 新疆维吾尔自治区

  • 伊犁哈萨克自治州
  • 新疆维吾尔自治区

  • 克孜勒苏柯尔克孜自治州
  • 新疆维吾尔自治区

  • 克拉玛依市
  • 新疆维吾尔自治区

  • 博尔塔拉蒙古自治州
  • 新疆维吾尔自治区

  • 吐鲁番市
  • 新疆维吾尔自治区

  • 和田地区
  • 新疆维吾尔自治区

  • 哈密市
  • 新疆维吾尔自治区

  • 喀什地区
  • 新疆维吾尔自治区

  • 塔城地区
  • 新疆维吾尔自治区

  • 巴音郭楞蒙古自治州
  • 新疆维吾尔自治区

  • 昌吉回族自治州
  • 新疆维吾尔自治区

  • 自治区直辖县级行政区划
  • 新疆维吾尔自治区

  • 阿克苏地区
  • 新疆维吾尔自治区

  • 阿勒泰地区
  • 江苏省

  • 南京市
  • 江苏省

  • 南通市
  • 江苏省

  • 宿迁市
  • 江苏省

  • 常州市
  • 江苏省

  • 徐州市
  • 江苏省

  • 扬州市
  • 江苏省

  • 无锡市
  • 江苏省

  • 泰州市
  • 江苏省

  • 淮安市
  • 江苏省

  • 盐城市
  • 江苏省

  • 苏州市
  • 江苏省

  • 连云港市
  • 江苏省

  • 镇江市
  • 江西省

  • 上饶市
  • 江西省

  • 九江市
  • 江西省

  • 南昌市
  • 江西省

  • 吉安市
  • 江西省

  • 宜春市
  • 江西省

  • 抚州市
  • 江西省

  • 新余市
  • 江西省

  • 景德镇市
  • 江西省

  • 萍乡市
  • 江西省

  • 赣州市
  • 江西省

  • 鹰潭市
  • 河北省

  • 保定市
  • 河北省

  • 唐山市
  • 河北省

  • 廊坊市
  • 河北省

  • 张家口市
  • 河北省

  • 承德市
  • 河北省

  • 沧州市
  • 河北省

  • 石家庄市
  • 河北省

  • 秦皇岛市
  • 河北省

  • 衡水市
  • 河北省

  • 邢台市
  • 河北省

  • 邯郸市
  • 河南省

  • 三门峡市
  • 河南省

  • 信阳市
  • 河南省

  • 南阳市
  • 河南省

  • 周口市
  • 河南省

  • 商丘市
  • 河南省

  • 安阳市
  • 河南省

  • 平顶山市
  • 河南省

  • 开封市
  • 河南省

  • 新乡市
  • 河南省

  • 洛阳市
  • 河南省

  • 漯河市
  • 河南省

  • 濮阳市
  • 河南省

  • 焦作市
  • 河南省

  • 省直辖县级行政区划
  • 河南省

  • 许昌市
  • 河南省

  • 郑州市
  • 河南省

  • 驻马店市
  • 河南省

  • 鹤壁市
  • 浙江省

  • 丽水市
  • 浙江省

  • 台州市
  • 浙江省

  • 嘉兴市
  • 浙江省

  • 宁波市
  • 浙江省

  • 杭州市
  • 浙江省

  • 温州市
  • 浙江省

  • 湖州市
  • 浙江省

  • 绍兴市
  • 浙江省

  • 舟山市
  • 浙江省

  • 衢州市
  • 浙江省

  • 金华市
  • 海南省

  • 三亚市
  • 海南省

  • 三沙市
  • 海南省

  • 儋州市
  • 海南省

  • 海口市
  • 海南省

  • 省直辖县级行政区划
  • 湖北省

  • 十堰市
  • 湖北省

  • 咸宁市
  • 湖北省

  • 孝感市
  • 湖北省

  • 宜昌市
  • 湖北省

  • 恩施土家族苗族自治州
  • 湖北省

  • 武汉市
  • 湖北省

  • 省直辖县级行政区划
  • 湖北省

  • 荆州市
  • 湖北省

  • 荆门市
  • 湖北省

  • 襄阳市
  • 湖北省

  • 鄂州市
  • 湖北省

  • 随州市
  • 湖北省

  • 黄冈市
  • 湖北省

  • 黄石市
  • 湖南省

  • 娄底市
  • 湖南省

  • 岳阳市
  • 湖南省

  • 常德市
  • 湖南省

  • 张家界市
  • 湖南省

  • 怀化市
  • 湖南省

  • 株洲市
  • 湖南省

  • 永州市
  • 湖南省

  • 湘潭市
  • 湖南省

  • 湘西土家族苗族自治州
  • 湖南省

  • 益阳市
  • 湖南省

  • 衡阳市
  • 湖南省

  • 邵阳市
  • 湖南省

  • 郴州市
  • 湖南省

  • 长沙市
  • 甘肃省

  • 临夏回族自治州
  • 甘肃省

  • 兰州市
  • 甘肃省

  • 嘉峪关市
  • 甘肃省

  • 天水市
  • 甘肃省

  • 定西市
  • 甘肃省

  • 平凉市
  • 甘肃省

  • 庆阳市
  • 甘肃省

  • 张掖市
  • 甘肃省

  • 武威市
  • 甘肃省

  • 甘南藏族自治州
  • 甘肃省

  • 白银市
  • 甘肃省

  • 酒泉市
  • 甘肃省

  • 金昌市
  • 甘肃省

  • 陇南市
  • 福建省

  • 三明市
  • 福建省

  • 南平市
  • 福建省

  • 厦门市
  • 福建省

  • 宁德市
  • 福建省

  • 泉州市
  • 福建省

  • 漳州市
  • 福建省

  • 福州市
  • 福建省

  • 莆田市
  • 福建省

  • 龙岩市
  • 西藏自治区

  • 山南市
  • 西藏自治区

  • 拉萨市
  • 西藏自治区

  • 日喀则市
  • 西藏自治区

  • 昌都市
  • 西藏自治区

  • 林芝市
  • 西藏自治区

  • 那曲市
  • 西藏自治区

  • 阿里地区
  • 贵州省

  • 六盘水市
  • 贵州省

  • 安顺市
  • 贵州省

  • 毕节市
  • 贵州省

  • 贵阳市
  • 贵州省

  • 遵义市
  • 贵州省

  • 铜仁市
  • 贵州省

  • 黔东南苗族侗族自治州
  • 贵州省

  • 黔南布依族苗族自治州
  • 贵州省

  • 黔西南布依族苗族自治州
  • 辽宁省

  • 丹东市
  • 辽宁省

  • 大连市
  • 辽宁省

  • 抚顺市
  • 辽宁省

  • 朝阳市
  • 辽宁省

  • 本溪市
  • 辽宁省

  • 沈阳市
  • 辽宁省

  • 盘锦市
  • 辽宁省

  • 营口市
  • 辽宁省

  • 葫芦岛市
  • 辽宁省

  • 辽阳市
  • 辽宁省

  • 铁岭市
  • 辽宁省

  • 锦州市
  • 辽宁省

  • 阜新市
  • 辽宁省

  • 鞍山市
  • 重庆市

  • 重庆市

  • 市辖区
  • 陕西省

  • 咸阳市
  • 陕西省

  • 商洛市
  • 陕西省

  • 安康市
  • 陕西省

  • 宝鸡市
  • 陕西省

  • 延安市
  • 陕西省

  • 榆林市
  • 陕西省

  • 汉中市
  • 陕西省

  • 渭南市
  • 陕西省

  • 西安市
  • 陕西省

  • 铜川市
  • 青海省

  • 果洛藏族自治州
  • 青海省

  • 海东市
  • 青海省

  • 海北藏族自治州
  • 青海省

  • 海南藏族自治州
  • 青海省

  • 海西蒙古族藏族自治州
  • 青海省

  • 玉树藏族自治州
  • 青海省

  • 西宁市
  • 青海省

  • 黄南藏族自治州
  • 黑龙江省

  • 七台河市
  • 黑龙江省

  • 伊春市
  • 黑龙江省

  • 佳木斯市
  • 黑龙江省

  • 双鸭山市
  • 黑龙江省

  • 哈尔滨市
  • 黑龙江省

  • 大兴安岭地区
  • 黑龙江省

  • 大庆市
  • 黑龙江省

  • 牡丹江市
  • 黑龙江省

  • 绥化市
  • 黑龙江省

  • 鸡西市
  • 黑龙江省

  • 鹤岗市
  • 黑龙江省

  • 黑河市
  • 黑龙江省

  • 齐齐哈尔市