当前位置: 首页 > news >正文

C++中std::allocator的使用案例详解

标准库中包含一个名为allocator的类,允许我们将分配和初始化分离。使用allocator通常会提供更好的性能和更灵活的内存管理能力。

new有一些灵活性上的局限,其中一方面表现在它将内存分配和对象构造组合在了一起。类似的,delete将对象析构和内存释放组合在了一起。我们分配单个对象时,通常希望将内存分配和对象初始化组合在一起。因为在这种情况下,我们几乎肯定知道对象应有什么值。当分配一大块内存时,我们通常计划在这块内存上按需构造对象。在此情况下,我们希望将内存分配和对象构造分离。这意味着我们可以分配大块内存,但只在真正需要时才真正执行对象的创建操作(同时付出一定开销)。一般情况下,将内存分配和对象构造组合在一起可能会导致不必要的浪费

标准库allocator类定义在头文件memory中,它帮助我们将内存分配和对象构造分离开来它提供一种类型感知的内存分配方法,它分配的内存是原始的、未构造的。类似vector,allocator是一个模板。为了定义一个allocator对象,我们必须指明这个allocator可以分配的对象类型。当一个allocator对象分配内存时,它会根据给定的对象类型来确定恰当的内存大小和对齐位置。allocator支持的操作,如下:

allocatro分配的内存是未构造的(unconstructed)。我们按需要在此内存中构造对象。在新标准库中,construct成员函数接受一个指针和零个或多个额外参数,在给定位置构造一个元素。额外参数用来初始化构造的对象。类似make_shared的参数,这些额外参数必须是与构造的对象的类型相匹配的合法的初始化器。

在早期版本的标准库中,construct只接受两个参数:指向创建对象位置的指针和一个元素类型的值。因此,我们只能将一个元素拷贝到未构造空间中,而不能用元素类型的任何其它构造函数来构造一个元素。还未构造对象的情况下就使用原始内存是错误的。为了使用allocator返回的内存,我们必须用construct构造对象。使用未构造的内存,其行为是未定义的。

当我们用完对象后,必须对每个构造的元素调用destroy来销毁它们。函数destroy接受一个指针,对执行的对象执行析构函数。我们只能对真正构造了的元素进行destroy操作。一旦元素被销毁后,就可以重新使用这部分内存来保存其它string,也可以将其归还给系统。释放内存通过调用deallocate来完成。我们传递给deallocate的指针不能为空,它必须指向由allocate分配的内存。而且,传递给deallocate的大小参数必须与调用allocate分配内存时提供的大小参数具有一样的值。

标准库还为allocator类定义了两个伴随算法,可以在未初始化内存中创建对象。它们都定义在头文件memory中,如下:

在C++中,内存是通过new表达式分配,通过delete表达式释放的。标准库还定义了一个allocator类来分配动态内存块。分配动态内存的程序应负责释放它所分配的内存。内存的正确释放是非常容易出错的地方:要么内存永远不会被释放,要么在仍有指针引用它时就被释放了。新的标准库定义了智能指针类型------shared_ptr、unique_ptr和weak_ptr,可令动态内存管理更为安全。对于一块内存,当没有任何用户使用它时,智能指针会自动释放它。现代C++程序应尽可能使用智能指针。

std::allocator是标准库容器的默认内存分配器。你可以替换自己的分配器,这允许你控制标准容器分配内存的方式。

以上内容主要摘自:《C++Primer(Fifth Edition 中文版)》第12.2.2章节

下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

#include "allocator.hpp"

#include <iostream>

#include <memory>

#include <string>

#include <vector>

namespaceallocator_ {

// reference: C++ Primer(Fifth Edition) 12.2.2

inttest_allocator_1()

{

std::allocator<std::string> alloc;// 可以分配string的allocator对象

intn{ 5 };

autoconstp = alloc.allocate(n);// 分配n个未初始化的string

auto q = p;// q指向最后构造的元素之后的位置

alloc.construct(q++);// *q为空字符串

alloc.construct(q++, 10,'c');// *q为cccccccccc

alloc.construct(q++,"hi");// *q为hi

std::cout << *p << std::endl;// 正确:使用string的输出运算符

//std::cout << *q << std::endl; // 灾难:q指向未构造的内存

std::cout << p[0] << std::endl;

std::cout << p[1] << std::endl;

std::cout << p[2] << std::endl;

while(q != p) {

alloc.destroy(--q);// 释放我们真正构造的string

}

alloc.deallocate(p, n);

return0;

}

inttest_allocator_2()

{

std::vector<int> vi{ 1, 2, 3, 4, 5 };

// 分配比vi中元素所占用空间大一倍的动态内存

std::allocator<int> alloc;

auto p = alloc.allocate(vi.size() * 2);

// 通过拷贝vi中的元素来构造从p开始的元素

/* 类似拷贝算法,uninitialized_copy接受三个迭代器参数。前两个表示输入序列,第三个表示

这些元素将要拷贝到的目的空间。传递给uninitialized_copy的目的位置迭代器必须指向未构造的

内存。与copy不同,uninitialized_copy在给定目的位置构造元素。

类似copy,uninitialized_copy返回(递增后的)目的位置迭代器。因此,一次uninitialized_copy调用

会返回一个指针,指向最后一个构造的元素之后的位置。

*/

auto q = std::uninitialized_copy(vi.begin(), vi.end(), p);

// 将剩余元素初始化为42

std::uninitialized_fill_n(q, vi.size(), 42);

return0;

}

// reference: http://www.modernescpp.com/index.php/memory-management-with-std-allocator

inttest_allocator_3()

{

std::cout << std::endl;

std::allocator<int> intAlloc;

std::cout <<"intAlloc.max_size(): "<< intAlloc.max_size() << std::endl;

int* intArray = intAlloc.allocate(100);

std::cout <<"intArray[4]: "<< intArray[4] << std::endl;

intArray[4] = 2011;

std::cout <<"intArray[4]: "<< intArray[4] << std::endl;

intAlloc.deallocate(intArray, 100);

std::cout << std::endl;

std::allocator<double> doubleAlloc;

std::cout <<"doubleAlloc.max_size(): "<< doubleAlloc.max_size() << std::endl;

std::cout << std::endl;

std::allocator<std::string> stringAlloc;

std::cout <<"stringAlloc.max_size(): "<< stringAlloc.max_size() << std::endl;

std::string* myString = stringAlloc.allocate(3);

stringAlloc.construct(myString,"Hello");

stringAlloc.construct(myString + 1,"World");

stringAlloc.construct(myString + 2,"!");

std::cout << myString[0] <<" "<< myString[1] <<" "<< myString[2] << std::endl;

stringAlloc.destroy(myString);

stringAlloc.destroy(myString + 1);

stringAlloc.destroy(myString + 2);

stringAlloc.deallocate(myString, 3);

std::cout << std::endl;

return0;

}

//

// reference: http://en.cppreference.com/w/cpp/memory/allocator

inttest_allocator_4()

{

std::allocator<int> a1;// default allocator for ints

int* a = a1.allocate(1);// space for one int

a1.construct(a, 7);// construct the int

std::cout << a[0] <<'\n';

a1.deallocate(a, 1);// deallocate space for one int

// default allocator for strings

std::allocator<std::string> a2;

// same, but obtained by rebinding from the type of a1

decltype(a1)::rebind<std::string>::other a2_1;

// same, but obtained by rebinding from the type of a1 via allocator_traits

std::allocator_traits<decltype(a1)>::rebind_alloc<std::string> a2_2;

std::string* s = a2.allocate(2);// space for 2 strings

a2.construct(s,"foo");

a2.construct(s + 1,"bar");

std::cout << s[0] <<' '<< s[1] <<'\n';

a2.destroy(s);

a2.destroy(s + 1);

a2.deallocate(s, 2);

return0;

}

}// namespace allocator_

http://www.gsyq.cn/news/1411195.html

相关文章:

  • 别再到处找教程了!Windows 10/11 保姆级 Mosquitto MQTT 服务器搭建(含MQTTX客户端连接测试)
  • 别再只会点Merge了!IntelliJ IDEA里用Rebase优雅解决Git冲突的完整流程
  • 选择命理推演软件,到底该看什么?
  • 别再混淆了!一文讲透嵌入式中间件与互联网中台的核心差异(附基站中间件实战API解析)
  • 易基因:Bioact Mater/IF20.3:华南理工大学王迎军院士团队RRBS等揭示DNA甲基化调控衰老骨缺损修复新机制
  • 从游戏图形到AI芯片:浮点数格式FP32/FP16/FP8的演进史与硬件设计启示
  • 避坑指南:从ToLua迁移到XLua,我踩过的那些‘坑’和最佳实践
  • Keil uVision彻底卸载指南:解决残留问题与注册表清理
  • 保姆级教程:用C++写个进程扫描器,揪出学生机房管理助手7.5的随机马甲
  • 【脑洞】是否可以存在一个“零知识储备”的大模型?一个像实习生的大模型
  • AI技能版本管理实战:像管理代码一样管理你的提示词与配置
  • Reactor反应堆模式
  • 别再给主力机装SQL Server了!用群晖Docker搭个2019版,开发测试两不误
  • VMware Workstation Pro 17免费激活完整指南:终极许可证密钥获取与配置
  • 原来昆明这些味道好的美食店,很多人竟然都不知道?
  • JTAG调试中nSRST信号连接的必要性与实践
  • RTX51 Tiny信号量实现与UART共享应用
  • 英语作文_8B
  • 告别GUI点点点:用Ansys命令流高效搞定点线面体建模(附常用命令清单)
  • 告别第三方录屏软件!用Unity Recorder实现4K多机位动画录制(附Timeline联动技巧)
  • 2026年 欧标镀锌钢板厂家推荐排行榜:EN 10346标准宝钢、山钢集团、烨辉品牌深度解析与选购指南 - 品牌企业推荐师(官方)
  • GTA5 人物模组超详细制作流程Blender+Sollumz建模转模全细节
  • MATLAB回归分析避坑指南:regress函数实战,从数据导入到结果解读(附完整代码)
  • 构建具备主动性的AI Agent系统
  • 详解C++编程中运算符的使用
  • 基于RISC-V架构的商业航天级MCU国产化技术路径与产业生态研究
  • 【408考研·数据结构专题】二叉树、树与森林、线索树及哈夫曼树核心考点与秒杀技巧深度总结
  • LLM应用工程化:将提示词与任务流视为代码管理的实践指南
  • 别再乱调参了!用sklearn的MLPClassifier/Regressor,这3个隐藏层配置技巧让你模型效果立竿见影
  • CGA老年综合评估MMSE量表标准化应用规范