-
Notifications
You must be signed in to change notification settings - Fork 0
/
C-多态分配器.html
1507 lines (1109 loc) · 126 KB
/
C-多态分配器.html
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="keywords" content="C++多态分配器, 技术分享,学习记录。">
<meta name="description" content="C++多态分配器,调试资源和自定义类型">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta name="renderer" content="webkit|ie-stand|ie-comp">
<meta name="mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="referrer" content="no-referrer-when-downgrade">
<!-- Global site tag (gtag.js) - Google Analytics -->
<title>C++多态分配器 | 操作系统技术分享</title>
<link rel="icon" type="image/png" href="/favicon.png">
<link rel="stylesheet" type="text/css" href="/libs/awesome/css/all.min.css">
<link rel="stylesheet" type="text/css" href="/libs/materialize/materialize.min.css">
<link rel="stylesheet" type="text/css" href="/libs/aos/aos.css">
<link rel="stylesheet" type="text/css" href="/libs/animate/animate.min.css">
<link rel="stylesheet" type="text/css" href="/libs/lightGallery/css/lightgallery.min.css">
<link rel="stylesheet" type="text/css" href="/css/matery.css">
<link rel="stylesheet" type="text/css" href="/css/my.css">
<script src="/libs/jquery/jquery-3.6.0.min.js"></script>
<meta name="generator" content="Hexo 5.4.1"></head>
<body>
<header class="navbar-fixed">
<nav id="headNav" class="bg-color nav-transparent">
<div id="navContainer" class="nav-wrapper container">
<div class="brand-logo">
<a href="/" class="waves-effect waves-light">
<img src="/medias/logo.png" class="logo-img" alt="LOGO">
<span class="logo-span">操作系统技术分享</span>
</a>
</div>
<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
<li class="hide-on-med-and-down nav-item">
<a href="/" class="waves-effect waves-light">
<i class="fas fa-home" style="zoom: 0.6;"></i>
<span>首页</span>
</a>
</li>
<li class="hide-on-med-and-down nav-item">
<a href="/tags" class="waves-effect waves-light">
<i class="fas fa-tags" style="zoom: 0.6;"></i>
<span>标签</span>
</a>
</li>
<li class="hide-on-med-and-down nav-item">
<a href="/categories" class="waves-effect waves-light">
<i class="fas fa-bookmark" style="zoom: 0.6;"></i>
<span>分类</span>
</a>
</li>
<li class="hide-on-med-and-down nav-item">
<a href="/archives" class="waves-effect waves-light">
<i class="fas fa-archive" style="zoom: 0.6;"></i>
<span>归档</span>
</a>
</li>
<li class="hide-on-med-and-down nav-item">
<a href="/about" class="waves-effect waves-light">
<i class="fas fa-user-circle" style="zoom: 0.6;"></i>
<span>关于</span>
</a>
</li>
<li>
<a href="#searchModal" class="modal-trigger waves-effect waves-light">
<i id="searchIcon" class="fas fa-search" title="搜索" style="zoom: 0.85;"></i>
</a>
</li>
</ul>
<div id="mobile-nav" class="side-nav sidenav">
<div class="mobile-head bg-color">
<img src="/medias/logo.png" class="logo-img circle responsive-img">
<div class="logo-name">操作系统技术分享</div>
<div class="logo-desc">
这里是农光鑫的个人博客
</div>
</div>
<ul class="menu-list mobile-menu-list">
<li class="m-nav-item">
<a href="/" class="waves-effect waves-light">
<i class="fa-fw fas fa-home"></i>
首页
</a>
</li>
<li class="m-nav-item">
<a href="/tags" class="waves-effect waves-light">
<i class="fa-fw fas fa-tags"></i>
标签
</a>
</li>
<li class="m-nav-item">
<a href="/categories" class="waves-effect waves-light">
<i class="fa-fw fas fa-bookmark"></i>
分类
</a>
</li>
<li class="m-nav-item">
<a href="/archives" class="waves-effect waves-light">
<i class="fa-fw fas fa-archive"></i>
归档
</a>
</li>
<li class="m-nav-item">
<a href="/about" class="waves-effect waves-light">
<i class="fa-fw fas fa-user-circle"></i>
关于
</a>
</li>
<li><div class="divider"></div></li>
<li>
<a href="https://gitee.com/nongguangxin" class="waves-effect waves-light" target="_blank">
<i class="fab fa-github-square fa-fw"></i>Fork Me
</a>
</li>
</ul>
</div>
</div>
<style>
.nav-transparent .github-corner {
display: none !important;
}
.github-corner {
position: absolute;
z-index: 10;
top: 0;
right: 0;
border: 0;
transform: scale(1.1);
}
.github-corner svg {
color: #0f9d58;
fill: #fff;
height: 64px;
width: 64px;
}
.github-corner:hover .octo-arm {
animation: a 0.56s ease-in-out;
}
.github-corner .octo-arm {
animation: none;
}
@keyframes a {
0%,
to {
transform: rotate(0);
}
20%,
60% {
transform: rotate(-25deg);
}
40%,
80% {
transform: rotate(10deg);
}
}
</style>
<a href="https://gitee.com/nongguangxin" class="github-corner tooltipped hide-on-med-and-down" target="_blank"
data-tooltip="Fork Me" data-position="left" data-delay="50">
<svg viewBox="0 0 250 250" aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor" class="octo-body"></path>
</svg>
</a>
</nav>
</header>
<div class="bg-cover pd-header post-cover" style="background-image: url('/medias/featureimages/23.jpg')">
<div class="container" style="right: 0px;left: 0px;">
<div class="row">
<div class="col s12 m12 l12">
<div class="brand">
<h1 class="description center-align post-title">C++多态分配器</h1>
</div>
</div>
</div>
</div>
</div>
<main class="post-container content">
<link rel="stylesheet" href="/libs/tocbot/tocbot.css">
<style>
#articleContent h1::before,
#articleContent h2::before,
#articleContent h3::before,
#articleContent h4::before,
#articleContent h5::before,
#articleContent h6::before {
display: block;
content: " ";
height: 100px;
margin-top: -100px;
visibility: hidden;
}
#articleContent :focus {
outline: none;
}
.toc-fixed {
position: fixed;
top: 64px;
}
.toc-widget {
width: 345px;
padding-left: 20px;
}
.toc-widget .toc-title {
padding: 35px 0 15px 17px;
font-size: 1.5rem;
font-weight: bold;
line-height: 1.5rem;
}
.toc-widget ol {
padding: 0;
list-style: none;
}
#toc-content {
padding-bottom: 30px;
overflow: auto;
}
#toc-content ol {
padding-left: 10px;
}
#toc-content ol li {
padding-left: 10px;
}
#toc-content .toc-link:hover {
color: #42b983;
font-weight: 700;
text-decoration: underline;
}
#toc-content .toc-link::before {
background-color: transparent;
max-height: 25px;
position: absolute;
right: 23.5vw;
display: block;
}
#toc-content .is-active-link {
color: #42b983;
}
#floating-toc-btn {
position: fixed;
right: 15px;
bottom: 76px;
padding-top: 15px;
margin-bottom: 0;
z-index: 998;
}
#floating-toc-btn .btn-floating {
width: 48px;
height: 48px;
}
#floating-toc-btn .btn-floating i {
line-height: 48px;
font-size: 1.4rem;
}
</style>
<div class="row">
<div id="main-content" class="col s12 m12 l9">
<!-- 文章内容详情 -->
<div id="artDetail">
<div class="card">
<div class="card-content article-info">
<div class="row tag-cate">
<div class="col s7">
<div class="article-tag">
<a href="/tags/%E5%A4%9A%E6%80%81%E5%88%86%E9%85%8D%E5%99%A8/">
<span class="chip bg-color">多态分配器</span>
</a>
<a href="/tags/%E5%86%85%E5%AD%98%E6%B1%A0/">
<span class="chip bg-color">内存池</span>
</a>
</div>
</div>
<div class="col s5 right-align">
<div class="post-cate">
<i class="fas fa-bookmark fa-fw icon-category"></i>
<a href="/categories/C/" class="post-category">
C++
</a>
</div>
</div>
</div>
<div class="post-info">
<div class="post-date info-break-policy">
<i class="far fa-calendar-minus fa-fw"></i>发布日期:
2021-10-09
</div>
<div class="post-date info-break-policy">
<i class="far fa-calendar-check fa-fw"></i>更新日期:
2022-03-01
</div>
<div class="info-break-policy">
<i class="far fa-file-word fa-fw"></i>文章字数:
4.6k
</div>
<div class="info-break-policy">
<i class="far fa-clock fa-fw"></i>阅读时长:
21 分
</div>
</div>
</div>
<hr class="clearfix">
<!-- 是否加载使用自带的 prismjs. -->
<link rel="stylesheet" href="/libs/prism/prism.min.css">
<div class="card-content article-card-content">
<div id="articleContent">
<h2 id="C-17-多态分配器,调试资源和自定义类型"><a href="#C-17-多态分配器,调试资源和自定义类型" class="headerlink" title="C++17: 多态分配器,调试资源和自定义类型"></a>C++17: 多态分配器,调试资源和自定义类型</h2><p>从C++17开始支持的多态分配器是标准库对标准分配器的增强。</p>
<p>它比常规分配器容易使用得多,并且允许相同的类型容器具有不同的分配器,甚至有可能在运行时改变分配器。</p>
<p>多态分配器符合标准库分配器的规则。尽管如此,它的核心还是使用内存资源对象来进行内存管理。</p>
<p>多态分配器包含指向内存资源类的指针,这就是为什么它可以使用虚函数调度。您可以在运行时更改内存资源,同时保留分配器的类型。这与常规分配器相反,后者使用不同分配器时是不同类型。</p>
<p>多态分配器的所有类型都位于单独的命名空间<code>std::pmr</code>在头文件<code><memory_resource></code>(PMR, Polymorphic Memory Resource)中。</p>
<h2 id="pmr核心要素"><a href="#pmr核心要素" class="headerlink" title="pmr核心要素"></a><code>pmr</code>核心要素</h2><p><code>pmr</code>的主要部分:</p>
<ul>
<li><code>std::pmr::memory_resource</code> - 是所有其他实现的抽象基类。它定义了以下纯函数: <pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">virtual</span> <span class="token keyword">void</span><span class="token operator">*</span> <span class="token function">do_allocate</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>size_t bytes<span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span>size_t alignment<span class="token punctuation">)</span>
<span class="token keyword">virtual</span> <span class="token keyword">void</span> <span class="token function">do_deallocate</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token operator">*</span> p<span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span>size_t bytes<span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span>size_t alignment<span class="token punctuation">)</span>
<span class="token keyword">virtual</span> <span class="token keyword">bool</span> <span class="token function">do_is_equal</span><span class="token punctuation">(</span><span class="token keyword">const</span> std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>memory_resource<span class="token operator">&</span> other<span class="token punctuation">)</span> <span class="token keyword">const</span> <span class="token keyword">noexcept</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>
</li>
<li><code>std::pmr::polymorphic_allocator</code> - 是标准分配器的实现,它使用 <code>memory_resource</code> 对象来管理内存分配和释放。</li>
<li><code>new_delete_resource()</code>和<code>null_memory_resource()</code>访问的是全局内存资源。</li>
<li>一组预先定义的内存池资源类:<ul>
<li><code>synchronized_pool_resource</code></li>
<li><code>unsynchronized_pool_resource</code></li>
<li><code>monotonic_buffer_resource</code></li>
</ul>
</li>
<li>模板特化的标准容器与多态分配器,例如<code>std::pmr::vector</code>,<code>std::pmr::string</code>,<code>std::pmr::map</code>等。每个特化容器都在与相应容器相同的头文件中定义。</li>
<li>同样值得一提的是,池资源可以链式包含。如果资源池中没有可用内存,分配器将从”上游”资源中分配。</li>
</ul>
<h2 id="预先定义的内存资源"><a href="#预先定义的内存资源" class="headerlink" title="预先定义的内存资源"></a>预先定义的内存资源</h2><h3 id="1-new-delete-resource"><a href="#1-new-delete-resource" class="headerlink" title="1. new_delete_resource()"></a>1. <code>new_delete_resource()</code></h3><p>它是一个函数,返回指向全局“默认”内存资源的指针。它使用全局 new 和 delete 管理内存。</p>
<h3 id="2-null-memory-resource"><a href="#2-null-memory-resource" class="headerlink" title="2. null_memory_resource()"></a>2. <code>null_memory_resource()</code></h3><p>它返回一个指向全局“空”内存资源的指针,它在每次分配时抛出<code>std::bad_alloc</code>。虽然听起来没什么用,但当你想保证你的对象不会在堆上分配任何内存时,它可能会很方便。或用于测试。</p>
<h3 id="3-synchronized-pool-resource"><a href="#3-synchronized-pool-resource" class="headerlink" title="3. synchronized_pool_resource"></a>3. synchronized_pool_resource</h3><p>这是一个线程安全的分配器,用于管理不同大小的池。每个池都是一组块,这些块被分成大小均匀的块。</p>
<h3 id="4-unsynchronized-pool-resource"><a href="#4-unsynchronized-pool-resource" class="headerlink" title="4. unsynchronized_pool_resource"></a>4. unsynchronized_pool_resource</h3><p>非线程安全的资源池。</p>
<h3 id="5-monotonic-buffer-resource"><a href="#5-monotonic-buffer-resource" class="headerlink" title="5. monotonic_buffer_resource"></a>5. monotonic_buffer_resource</h3><p>这是一个非线程安全的、快速的、特殊用途的资源,它从预先分配的缓冲区中获取内存,但不会通过release来释放它。它只能单向递增。</p>
<h2 id="多态分配器示例"><a href="#多态分配器示例" class="headerlink" title="多态分配器示例"></a>多态分配器示例</h2><p>使用<code>monotonic_buffer_resource</code>和<code>pmr::vector</code>的简单例子.</p>
<pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><iostream></span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><memory_resource></span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><vector></span></span>
<span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">int</span> argc<span class="token punctuation">,</span> <span class="token keyword">char</span><span class="token operator">*</span> argv<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">char</span> buffer<span class="token punctuation">[</span><span class="token number">64</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span><span class="token function">fill_n</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span><span class="token function">begin</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span><span class="token function">size</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token char">'-'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> buffer <span class="token operator"><<</span> std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>monotonic_buffer_resource pool<span class="token punctuation">{</span>std<span class="token double-colon punctuation">::</span><span class="token function">data</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">,</span>
std<span class="token double-colon punctuation">::</span><span class="token function">size</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>vector<span class="token operator"><</span><span class="token keyword">char</span><span class="token operator">></span> vec<span class="token punctuation">{</span><span class="token operator">&</span>pool<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">char</span> i <span class="token operator">=</span> <span class="token char">'a'</span><span class="token punctuation">;</span> i <span class="token operator"><=</span> <span class="token char">'z'</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
vec<span class="token punctuation">.</span><span class="token function">push_back</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> buffer <span class="token operator"><<</span> std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>输出:</p>
<pre class="line-numbers language-none"><code class="language-none">---------------------------------------------------------------
aababcdabcdefghabcdefghijklmnopabcdefghijklmnopqrstuvwxyz------<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>
<p>在上面的例子中,我们使用了一个<code>monotonic_buffer_resource</code>,这个资源是用堆栈中的一个内存块(<code>buffer[]</code>)初始化的。通过使用一个简单的 <code>char buffer[]</code> 数组,我们可以轻松打印“内存”的内容。<code>vector</code>从池中获取内存(并且由于它在堆栈上,所以速度非常快),如果没有更多可用空间,它将从“上游”资源中请求内存。该示例显示了需要插入更多元素时的<code>vector</code>重新分配。向量每次获取更多空间来存放所有字母。正如您所见,<code>monotonic_buffer_resource</code>不会删除任何内存,它只会向前增长。</p>
<p>我们也可以在向量上使用<code>reserve()</code>,这会限制内存分配的数量,但这个例子的重点是说明容器的“扩展”。</p>
<p>在容器中存储比<code>char</code>大的类型会怎么样?</p>
<h3 id="存储pmr-string"><a href="#存储pmr-string" class="headerlink" title="存储pmr::string"></a>存储<code>pmr::string</code></h3><p>将字符串插入<code>pmr::vector</code>会怎么样?</p>
<p>多态分配器的好处在于,如果容器中的对象也使用多态分配器,那么它们将要求父容器的分配器来管理内存。</p>
<p>如果你想使用这个特性,你必须使用<code>std::pmr::string</code>而不是<code>std::string</code>.</p>
<p>请查看下面的示例,其中我们预先在堆栈上分配缓冲区,然后将其传递到字符串<code>vector</code>:</p>
<pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><iostream></span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><memory_resource></span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><vector></span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><string></span></span>
<span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">int</span> argc<span class="token punctuation">,</span> <span class="token keyword">char</span><span class="token operator">*</span> argv<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token string">"sizeof(std::string): "</span> <span class="token operator"><<</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>string<span class="token punctuation">)</span> <span class="token operator"><<</span> <span class="token char">'\n'</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token string">"sizeof(std::pmr::string): "</span> <span class="token operator"><<</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>string<span class="token punctuation">)</span>
<span class="token operator"><<</span> std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span>
<span class="token keyword">char</span> buffer<span class="token punctuation">[</span><span class="token number">256</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">// a small buffer on the stack</span>
std<span class="token double-colon punctuation">::</span><span class="token function">fill_n</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span><span class="token function">begin</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span><span class="token function">size</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token char">'-'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token keyword">auto</span> BufferPrinter <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>string_view buf<span class="token punctuation">,</span>
std<span class="token double-colon punctuation">::</span>string_view title<span class="token punctuation">)</span> <span class="token punctuation">{</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> title <span class="token operator"><<</span> <span class="token string">":\n"</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">auto</span><span class="token operator">&</span> ch <span class="token operator">:</span> buf<span class="token punctuation">)</span> <span class="token punctuation">{</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token punctuation">(</span>ch <span class="token operator">>=</span> <span class="token char">' '</span> <span class="token operator">?</span> ch <span class="token operator">:</span> <span class="token char">'#'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token function">BufferPrinter</span><span class="token punctuation">(</span>buffer<span class="token punctuation">,</span> <span class="token string">"zeroed buffer"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>monotonic_buffer_resource pool<span class="token punctuation">{</span>std<span class="token double-colon punctuation">::</span><span class="token function">data</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">,</span>
std<span class="token double-colon punctuation">::</span><span class="token function">size</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>vector<span class="token operator"><</span>std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>string<span class="token operator">></span> vec<span class="token punctuation">{</span><span class="token operator">&</span>pool<span class="token punctuation">}</span><span class="token punctuation">;</span>
vec<span class="token punctuation">.</span><span class="token function">reserve</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
vec<span class="token punctuation">.</span><span class="token function">push_back</span><span class="token punctuation">(</span><span class="token string">"Hello World"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
vec<span class="token punctuation">.</span><span class="token function">push_back</span><span class="token punctuation">(</span><span class="token string">"One Two Three"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">BufferPrinter</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span><span class="token function">string_view</span><span class="token punctuation">(</span>buffer<span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span><span class="token function">size</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token string">"after two short strings"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
vec<span class="token punctuation">.</span><span class="token function">emplace_back</span><span class="token punctuation">(</span><span class="token string">"This is a longer string"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">BufferPrinter</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span><span class="token function">string_view</span><span class="token punctuation">(</span>buffer<span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span><span class="token function">size</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token string">"after longer string strings"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
vec<span class="token punctuation">.</span><span class="token function">push_back</span><span class="token punctuation">(</span><span class="token string">"Four Five Six"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">BufferPrinter</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span><span class="token function">string_view</span><span class="token punctuation">(</span>buffer<span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span><span class="token function">size</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token string">"after the last string"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>以下是我在gcc 11.1 上收到的输出</p>
<pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">sizeof<span class="token punctuation">(</span>std::string<span class="token punctuation">)</span>: <span class="token number">32</span>
sizeof<span class="token punctuation">(</span>std::pmr::string<span class="token punctuation">)</span>: <span class="token number">40</span>
zeroed buffer:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
after two short strings:
<span class="token variable"><span class="token variable">`</span><span class="token comment">#!6#####!6###########Hello World####</span><span class="token variable">`</span></span><span class="token comment">#!6#####!6###########One Two Three###-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------#</span>
after longer string strings:
<span class="token variable"><span class="token variable">`</span><span class="token comment">#!6#####!6###########Hello World####</span><span class="token variable">`</span></span><span class="token comment">#!6#####!6###########One Two Three###`#!6#####!6###################----------------------------------------------------------------------------------------This is a longer string#-------------------------------#</span>
after the last string:
<span class="token variable"><span class="token variable">`</span><span class="token comment">#!6#####!6###########Hello World####</span><span class="token variable">`</span></span><span class="token comment">#!6#####!6###########One Two Three###`#!6#####!6###################--------`#!6###`#!6###########Four Five Six###----------------------------------------This is a longer string#-------------------------------#</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>以下是这个例子观察到的主要事情:</p>
<ul>
<li><code>pmr::string</code>大小大于常规<code>std::string</code>。这是因为分配器不是无状态的,它必须存储指向内存资源的指针。</li>
<li>例子中的<code>vector</code>保留了五个元素,因此当插入四个元素时<code>vector</code>不会变长。</li>
<li>前两个字符串很短,因此它们可以放入<code>vector</code>的内存块中,此处没有动态内存分配。</li>
<li>但是对于第三个字符串,要求它是一个单独的内存块,并且<code>vector</code>只存储一个指向它的指针。正如您在输出中所见,`”This is a longer string”几乎位于缓冲区的末尾。</li>
<li>当我们插入另一个短字符串,然后它再次进入<code>vector</code>内存块。</li>
</ul>
<p>比较一下,下面使用常规<code>std::string</code>时的输出:</p>
<pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">sizeof<span class="token punctuation">(</span>std::string<span class="token punctuation">)</span>: <span class="token number">32</span>
sizeof<span class="token punctuation">(</span>std::pmr::string<span class="token punctuation">)</span>: <span class="token number">40</span>
zeroed buffer:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
after two short strings:
@l<span class="token comment">#############Hello World####`l#############One Two Three###-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------#</span>
after longer string strings:
@l<span class="token comment">#############Hello World####`l#############One Two Three#####n3#V##################-----------------------------------------------------------------------------------------------------------------------------------------------------------------------#</span>
after the last string:
@l<span class="token comment">#############Hello World####`l#############One Two Three#####n3#V##################--------#l#############Four Five Six###-------------------------------------------------------------------------------------------------------------------------------#</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>这次容器中的元素使用更少的内存,因为不需要存储指向内存资源的指针。短字符串存储在<code>vector</code>的内存块中,但请注意较长的字符串……它不在缓冲区中!正确地说,向量存储一个指向分配长字符串的内存块的指针,但默认分配器分配了它,因此它不会出现在我们的输出中。</p>
<p>我提到,如果内存资源不够,则分配器将从上游资源获取内存。我们如何观察它?</p>
<h3 id="一些手段"><a href="#一些手段" class="headerlink" title="一些手段"></a>一些手段</h3><p>在前面的例子中,上游内存资源是默认的,因为我们没有改变它。这意味着使用的是new() 和 delete()。但是,我们必须记住, do_allocate() 和 do_deallocate() 成员函数也采用对齐参数。</p>
<p>这就是为什么如果我们想要查看内存是否由 <code>new()</code> 分配,我们必须使用 C++17 带对齐支持的<code>new()</code> :</p>
<pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><iostream></span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><memory_resource></span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><vector></span></span>
<span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">include</span> <span class="token string"><string></span></span>
<span class="token keyword">void</span><span class="token operator">*</span> lastAllocatedPtr <span class="token operator">=</span> <span class="token keyword">nullptr</span><span class="token punctuation">;</span>
size_t lastSize <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token keyword">void</span><span class="token operator">*</span> <span class="token keyword">operator</span> <span class="token keyword">new</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>size_t size<span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span>align_val_t align<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">auto</span> ptr <span class="token operator">=</span> <span class="token function">aligned_alloc</span><span class="token punctuation">(</span><span class="token generic-function"><span class="token function">static_cast</span><span class="token generic class-name"><span class="token operator"><</span>std<span class="token double-colon punctuation">::</span>size_t<span class="token operator">></span></span></span><span class="token punctuation">(</span>align<span class="token punctuation">)</span><span class="token punctuation">,</span> size<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>ptr<span class="token punctuation">)</span> <span class="token keyword">throw</span> std<span class="token double-colon punctuation">::</span>bad_alloc<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token string">"new: "</span> <span class="token operator"><<</span> size
<span class="token operator"><<</span> <span class="token string">", align: "</span> <span class="token operator"><<</span> <span class="token generic-function"><span class="token function">static_cast</span><span class="token generic class-name"><span class="token operator"><</span>std<span class="token double-colon punctuation">::</span>size_t<span class="token operator">></span></span></span><span class="token punctuation">(</span>align<span class="token punctuation">)</span>
<span class="token operator"><<</span> <span class="token string">", ptr: "</span> <span class="token operator"><<</span> ptr <span class="token operator"><<</span> <span class="token char">'\n'</span><span class="token punctuation">;</span>
lastAllocatedPtr <span class="token operator">=</span> ptr<span class="token punctuation">;</span>
lastSize <span class="token operator">=</span> size<span class="token punctuation">;</span>
<span class="token keyword">return</span> ptr<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">void</span> <span class="token keyword">operator</span> <span class="token keyword">delete</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token operator">*</span> ptr<span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span>size_t size<span class="token punctuation">,</span>
std<span class="token double-colon punctuation">::</span>align_val_t align<span class="token punctuation">)</span> <span class="token keyword">noexcept</span>
<span class="token punctuation">{</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token string">"delete: "</span> <span class="token operator"><<</span> size
<span class="token operator"><<</span> <span class="token string">", align: "</span> <span class="token operator"><<</span> <span class="token generic-function"><span class="token function">static_cast</span><span class="token generic class-name"><span class="token operator"><</span>std<span class="token double-colon punctuation">::</span>size_t<span class="token operator">></span></span></span><span class="token punctuation">(</span>align<span class="token punctuation">)</span>
<span class="token operator"><<</span> <span class="token string">", ptr : "</span> <span class="token operator"><<</span> ptr <span class="token operator"><<</span> <span class="token char">'\n'</span><span class="token punctuation">;</span>
<span class="token function">free</span><span class="token punctuation">(</span>ptr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">int</span> argc<span class="token punctuation">,</span> <span class="token keyword">char</span><span class="token operator">*</span> argv<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">constexpr</span> size_t buf_size <span class="token operator">=</span> <span class="token number">32</span><span class="token punctuation">;</span>
<span class="token keyword">uint16_t</span> buffer<span class="token punctuation">[</span>buf_size<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">// a small buffer on the stack</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>monotonic_buffer_resource pool<span class="token punctuation">{</span>
std<span class="token double-colon punctuation">::</span><span class="token function">data</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span><span class="token function">size</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span><span class="token keyword">uint16_t</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>vector<span class="token operator"><</span><span class="token keyword">uint16_t</span><span class="token operator">></span> vec<span class="token punctuation">{</span><span class="token operator">&</span>pool<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span>size_t i <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator"><=</span> <span class="token number">20</span><span class="token punctuation">;</span> <span class="token operator">++</span>i<span class="token punctuation">)</span> vec<span class="token punctuation">.</span><span class="token function">push_back</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span>size_t i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> buf_size<span class="token punctuation">;</span> <span class="token operator">++</span>i<span class="token punctuation">)</span> std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> buffer<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator"><<</span> <span class="token string">" "</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span>
<span class="token keyword">auto</span><span class="token operator">*</span> bufTemp <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">uint16_t</span><span class="token operator">*</span><span class="token punctuation">)</span>lastAllocatedPtr<span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span>size_t i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> lastSize<span class="token punctuation">;</span> <span class="token operator">++</span>i<span class="token punctuation">)</span> std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> bufTemp<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator"><<</span> <span class="token string">" "</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> std<span class="token double-colon punctuation">::</span>endl<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>在上面的代码中实现了对齐的<code>new()</code></p>
<p>还有两个丑陋的全局变量 :) 然而,多亏了它们,我们可以看到我们的内存何时消失.</p>
<p>这一次,我们存储<code>uint16_t</code>而不是<code>char</code>.</p>
<p>该程序试图在一个向量中存储 20 个数字,但由于向量不断增长,我们需要的不仅仅是预定义的缓冲区(只有 32 )。这就是为什么在某些时候分配器会转向全局<code>new</code>和<code>delete</code>。</p>
<p>以下可能获得的输出:</p>
<pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">new: <span class="token number">128</span>, align: <span class="token number">16</span>, ptr: 0x55e22faebeb0
<span class="token number">1</span> <span class="token number">1</span> <span class="token number">2</span> <span class="token number">1</span> <span class="token number">2</span> <span class="token number">3</span> <span class="token number">4</span> <span class="token number">1</span> <span class="token number">2</span> <span class="token number">3</span> <span class="token number">4</span> <span class="token number">5</span> <span class="token number">6</span> <span class="token number">7</span> <span class="token number">8</span> <span class="token number">1</span> <span class="token number">2</span> <span class="token number">3</span> <span class="token number">4</span> <span class="token number">5</span> <span class="token number">6</span> <span class="token number">7</span> <span class="token number">8</span> <span class="token number">9</span> <span class="token number">10</span> <span class="token number">11</span> <span class="token number">12</span> <span class="token number">13</span> <span class="token number">14</span> <span class="token number">15</span> <span class="token number">16</span> <span class="token number">0</span>
<span class="token number">1</span> <span class="token number">2</span> <span class="token number">3</span> <span class="token number">4</span> <span class="token number">5</span> <span class="token number">6</span> <span class="token number">7</span> <span class="token number">8</span> <span class="token number">9</span> <span class="token number">10</span> <span class="token number">11</span> <span class="token number">12</span> <span class="token number">13</span> <span class="token number">14</span> <span class="token number">15</span> <span class="token number">16</span> <span class="token number">17</span> <span class="token number">18</span> <span class="token number">19</span> <span class="token number">20</span> <span class="token number">0</span> <span class="token number">0</span> <span class="token number">0</span> <span class="token number">0</span> <span class="token number">0</span> <span class="token number">0</span> <span class="token punctuation">..</span><span class="token punctuation">..</span><span class="token punctuation">..</span>
delete: <span class="token number">128</span>, align: <span class="token number">16</span>, ptr <span class="token builtin class-name">:</span> 0x55e22faebeb0<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>
<p>看起来预定义的缓冲区最多只能存储第 16 个元素,当我们插入数字 17 时,向量必须增长,这就是我们看到新分配128 字节的原因。第二行显示自定义缓冲区的内容,而第三行显示通过<code>new() </code>分配的内存。</p>
<h3 id="更好的解决方案"><a href="#更好的解决方案" class="headerlink" title="更好的解决方案"></a>更好的解决方案</h3><p>前面的示例有效并向我们展示了一些东西,但是在生产代码中使用<code>new()</code> 和 <code>delete()</code> 进行特殊操作并不是您应该做的。事实上,内存资源是可扩展的,如果你想要最好的解决方案,你可以重用你的资源!</p>
<p>您所要做的就是实现以下内容:</p>
<ul>
<li>继承<code>std::pmr::memory_resource</code></li>
<li>实现相关接口:<ul>
<li><code>do_allocate()</code></li>
<li><code>do_deallocate()</code></li>
<li><code>do_is_equal()</code></li>
</ul>
</li>
<li>将自定义内存资源设置为对象和容器的资源池。</li>
</ul>
<p>下面是一些如何实现的参考:</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://www.youtube.com/watch?v=v3dz-AKOVL8">CppCon 2017: Pablo Halpern “Allocators: The Good Parts” - YouTube</a></li>
<li><a target="_blank" rel="noopener" href="https://www.youtube.com/watch?v=FcpmMmyNNv8">Taming dynamic memory - An introduction to custom allocators in C++ - Andreas Weis - code::dive 2018 - YouTube</a></li>
<li>A whole extensive chapter in Nicolai’s book on C++17: <a target="_blank" rel="noopener" href="https://leanpub.com/cpp17">C++17 - The Complete Guide</a>.</li>
<li><a target="_blank" rel="noopener" href="https://www.youtube.com/watch?v=q6A7cKFXjY0&">C++ Weekly - Ep 222 - 3.5x Faster Standard Containers With PMR! - YouTube</a></li>
</ul>
<h3 id="调试内存资源"><a href="#调试内存资源" class="headerlink" title="调试内存资源"></a>调试内存资源</h3><p>为了有效地使用分配器,如果有一个工具可以让我们跟踪容器中的内存分配,这会很方便。</p>
<pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">class</span> <span class="token class-name">debug_resource</span> <span class="token operator">:</span> <span class="token base-clause"><span class="token keyword">public</span> std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span><span class="token class-name">memory_resource</span></span>
<span class="token punctuation">{</span>
<span class="token keyword">public</span><span class="token operator">:</span>
<span class="token keyword">explicit</span> <span class="token function">debug_resource</span><span class="token punctuation">(</span>
std<span class="token double-colon punctuation">::</span>string name<span class="token punctuation">,</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>memory_resource<span class="token operator">*</span> up <span class="token operator">=</span> std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span><span class="token function">get_default_resource</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token operator">:</span> _name<span class="token punctuation">{</span>std<span class="token double-colon punctuation">::</span><span class="token function">move</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">,</span> _upstream<span class="token punctuation">{</span>up<span class="token punctuation">}</span>
<span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">void</span><span class="token operator">*</span> <span class="token function">do_allocate</span><span class="token punctuation">(</span>size_t bytes<span class="token punctuation">,</span> size_t alignment<span class="token punctuation">)</span> <span class="token keyword">override</span>
<span class="token punctuation">{</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> _name <span class="token operator"><<</span> <span class="token string">" do_allocate(): "</span> <span class="token operator"><<</span> bytes <span class="token operator"><<</span> <span class="token char">'\n'</span><span class="token punctuation">;</span>
<span class="token keyword">void</span><span class="token operator">*</span> ret <span class="token operator">=</span> _upstream<span class="token operator">-></span><span class="token function">allocate</span><span class="token punctuation">(</span>bytes<span class="token punctuation">,</span> alignment<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> ret<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">void</span> <span class="token function">do_deallocate</span><span class="token punctuation">(</span><span class="token keyword">void</span><span class="token operator">*</span> ptr<span class="token punctuation">,</span> size_t bytes<span class="token punctuation">,</span> size_t alignment<span class="token punctuation">)</span> <span class="token keyword">override</span>
<span class="token punctuation">{</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> _name <span class="token operator"><<</span> <span class="token string">" do_deallocate(): "</span> <span class="token operator"><<</span> bytes <span class="token operator"><<</span> <span class="token char">'\n'</span><span class="token punctuation">;</span>
_upstream<span class="token operator">-></span><span class="token function">deallocate</span><span class="token punctuation">(</span>ptr<span class="token punctuation">,</span> bytes<span class="token punctuation">,</span> alignment<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">bool</span> <span class="token function">do_is_equal</span><span class="token punctuation">(</span>
<span class="token keyword">const</span> std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>memory_resource<span class="token operator">&</span> other<span class="token punctuation">)</span> <span class="token keyword">const</span> <span class="token keyword">noexcept</span> <span class="token keyword">override</span>
<span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token operator">==</span> <span class="token operator">&</span>other<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">private</span><span class="token operator">:</span>
std<span class="token double-colon punctuation">::</span>string _name<span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>memory_resource<span class="token operator">*</span> _upstream<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p><code>debug_resource</code>只是实际内存资源的包装器。正如在<code>do_allocate</code>/<code>do_deallocate</code>函数中看到的那样,我们只记录数字,然后使用上游资源进行实际内存分配。</p>
<p>示例用例:</p>
<pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">int</span> argc<span class="token punctuation">,</span> <span class="token keyword">char</span><span class="token operator">*</span> argv<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">constexpr</span> size_t BUF_SIZE <span class="token operator">=</span> <span class="token number">128</span><span class="token punctuation">;</span>
<span class="token keyword">char</span> buffer<span class="token punctuation">[</span>BUF_SIZE<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span><span class="token function">fill_n</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span><span class="token function">begin</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span><span class="token function">size</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token char">'-'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
debug_resource default_dbg<span class="token punctuation">{</span><span class="token string">"default"</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>monotonic_buffer_resource pool<span class="token punctuation">{</span>std<span class="token double-colon punctuation">::</span><span class="token function">data</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">,</span>
std<span class="token double-colon punctuation">::</span><span class="token function">size</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">&</span>default_dbg<span class="token punctuation">}</span><span class="token punctuation">;</span>
debug_resource dbg<span class="token punctuation">{</span><span class="token string">"pool"</span><span class="token punctuation">,</span> <span class="token operator">&</span>pool<span class="token punctuation">}</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>vector<span class="token operator"><</span>std<span class="token double-colon punctuation">::</span>string<span class="token operator">></span> strings<span class="token punctuation">{</span><span class="token operator">&</span>dbg<span class="token punctuation">}</span><span class="token punctuation">;</span>
strings<span class="token punctuation">.</span><span class="token function">emplace_back</span><span class="token punctuation">(</span><span class="token string">"Hello Short String"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
strings<span class="token punctuation">.</span><span class="token function">emplace_back</span><span class="token punctuation">(</span><span class="token string">"Hello Short String 2"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>输出:</p>
<pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">pool do_allocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">32</span>
pool do_allocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">64</span>
pool do_deallocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">32</span>
pool do_deallocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">64</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>
<p>上面我们使用了两次<code>debug_resource</code>,第一个“池”用于记录请求到<code>monotonic_buffer_resource</code>的每个分配。在输出中,您可以看到我们有两次分配和两次解除分配。</p>
<p>还有另一个<code>debug_resource</code> - <code>default</code>。这被配置为monotonic_buffer_resource 的父级。这意味着如果池需要分配。那么它必须通过我们的“default”对象请求内存。</p>
<p>如果您添加三个字符串,如下所示:</p>
<pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">strings<span class="token punctuation">.</span><span class="token function">emplace_back</span><span class="token punctuation">(</span><span class="token string">"Hello Short String"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
strings<span class="token punctuation">.</span><span class="token function">emplace_back</span><span class="token punctuation">(</span><span class="token string">"Hello Short String 2"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
strings<span class="token punctuation">.</span><span class="token function">emplace_back</span><span class="token punctuation">(</span><span class="token string">"Hello A bit longer String"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre>
<p>那么输出就不一样了:</p>
<pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">pool do_allocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">32</span>
pool do_allocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">64</span>
pool do_deallocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">32</span>
pool do_allocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">128</span>
default do_allocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">256</span>
pool do_deallocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">64</span>
pool do_deallocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">128</span>
default do_deallocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">256</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>这次您可以注意到,对于第三个字符串,我们预定义的小缓冲区内没有空间,这就是为什么<code>monotonic_buffer_resource</code>必须要求另外256个字节的“default”。</p>
<h2 id="自定义类型"><a href="#自定义类型" class="headerlink" title="自定义类型"></a>自定义类型</h2><p>定义一个自定义类型:</p>
<pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">struct</span> <span class="token class-name">Product</span> <span class="token punctuation">{</span>
std<span class="token double-colon punctuation">::</span>string name<span class="token punctuation">;</span>
<span class="token keyword">char</span> cost<span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>
<p>如果将它插入向量:</p>
<pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp">std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>vector<span class="token operator"><</span>Product<span class="token operator">></span> vec<span class="token punctuation">{</span><span class="token operator">&</span>pool<span class="token punctuation">}</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>然后,vector将使用提供的内存资源,但不会应用于自定义类型中。所以,如果 Product 必须为name分配内存,它将使用默认分配器。</p>
<p>我们必须“启用”我们的类型并让它知道分配器,以便它可以利用来自父容器的分配器。</p>
<p>配备了调试资源和一些“缓冲区打印技术”,我们现在可以检查我们的自定义类型是否与分配器一起工作。让我们来看看:</p>
<pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">struct</span> <span class="token class-name">SimpleProduct</span> <span class="token punctuation">{</span>
std<span class="token double-colon punctuation">::</span>string name<span class="token punctuation">;</span>
<span class="token keyword">char</span> cost<span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">int</span> argc<span class="token punctuation">,</span> <span class="token keyword">char</span><span class="token operator">*</span> argv<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">constexpr</span> size_t BUF_SIZE <span class="token operator">=</span> <span class="token number">256</span><span class="token punctuation">;</span>
<span class="token keyword">char</span> buffer<span class="token punctuation">[</span>BUF_SIZE<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span><span class="token function">fill_n</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span><span class="token function">begin</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span><span class="token function">size</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token char">'-'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token keyword">auto</span> BufferPrinter <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>string_view buf<span class="token punctuation">,</span>
std<span class="token double-colon punctuation">::</span>string_view title<span class="token punctuation">)</span> <span class="token punctuation">{</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> title <span class="token operator"><<</span> <span class="token string">":\n"</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span>size_t i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> buf<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token operator">++</span>i<span class="token punctuation">)</span> <span class="token punctuation">{</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token punctuation">(</span>buf<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">>=</span> <span class="token char">' '</span> <span class="token operator">?</span> buf<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">:</span> <span class="token char">'#'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>i <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">64</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token char">'\n'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token char">'\n'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token function">BufferPrinter</span><span class="token punctuation">(</span>buffer<span class="token punctuation">,</span> <span class="token string">"initial buffer"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
debug_resource default_dbg<span class="token punctuation">{</span><span class="token string">"default"</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>monotonic_buffer_resource pool<span class="token punctuation">{</span>std<span class="token double-colon punctuation">::</span><span class="token function">data</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">,</span>
std<span class="token double-colon punctuation">::</span><span class="token function">size</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">&</span>default_dbg<span class="token punctuation">}</span><span class="token punctuation">;</span>
debug_resource dbg<span class="token punctuation">{</span><span class="token string">"buffer"</span><span class="token punctuation">,</span> <span class="token operator">&</span>pool<span class="token punctuation">}</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>vector<span class="token operator"><</span>SimpleProduct<span class="token operator">></span> products<span class="token punctuation">{</span><span class="token operator">&</span>dbg<span class="token punctuation">}</span><span class="token punctuation">;</span>
products<span class="token punctuation">.</span><span class="token function">reserve</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
products<span class="token punctuation">.</span><span class="token function">emplace_back</span><span class="token punctuation">(</span>SimpleProduct<span class="token punctuation">{</span><span class="token string">"car"</span><span class="token punctuation">,</span> <span class="token char">'7'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
products<span class="token punctuation">.</span><span class="token function">emplace_back</span><span class="token punctuation">(</span>SimpleProduct<span class="token punctuation">{</span><span class="token string">"TV"</span><span class="token punctuation">,</span> <span class="token char">'9'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
products<span class="token punctuation">.</span><span class="token function">emplace_back</span><span class="token punctuation">(</span>SimpleProduct<span class="token punctuation">{</span><span class="token string">"a bit longer product name"</span><span class="token punctuation">,</span> <span class="token char">'4'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">BufferPrinter</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>string_view<span class="token punctuation">{</span>buffer<span class="token punctuation">,</span> BUF_SIZE<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token string">"after insertion"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>输出:</p>
<pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">initial buffer:
----------------------------------------------------------------
----------------------------------------------------------------
----------------------------------------------------------------
---------------------------------------------------------------
buffer do_allocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">160</span>
after insertion:
<span class="token comment">###############car#er##@P#####7-------###############TV##er##</span>
@P<span class="token comment">#####9-------##N##U##################--------4---------------</span>
----------------------------------------------------------------
---------------------------------------------------------------<span class="token comment">#</span>
buffer do_deallocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">160</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>让我们解密代码和输出:</p>
<p>该<code>vector</code>包含<code>SimpleProduct</code>对象,它只是一个字符串和一个数字。我们保留了四个元素,您可以注意到我们的调试资源记录了 160 字节的分配。插入三个元素后,我们可以发现car和数字 7(这就是我使用 char 作为价格类型的原因)。然后是 9 的TV。我们也可以注意到<code>4</code>作为第三个元素的价格,但没有名字<code>a bit longer product name</code></p>
<p>让自定义类型分配器感知并不是很难,但我们必须记住以下几点:</p>
<ul>
<li>尽可能使用 pmr::* 类型,以便您可以将分配器传递给它们。</li>
<li>声明 allocator_type 以便分配器特征可以“识别”您的类型使用分配器。您还可以为分配器特征声明其他属性,但在大多数情况下,默认值就可以了。</li>
<li>声明接受分配器并将其进一步传递给您的成员的构造函数。</li>
<li>也注意声明分配器的复制和移动构造函数。</li>
<li>与分配和移动操作相同。</li>
</ul>
<p>这意味着我们相对简单的自定义类型声明必须变长:</p>
<pre class="line-numbers language-cpp" data-language="cpp"><code class="language-cpp"><span class="token keyword">struct</span> <span class="token class-name">Product</span> <span class="token punctuation">{</span>
<span class="token keyword">using</span> allocator_type <span class="token operator">=</span> std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>polymorphic_allocator<span class="token operator"><</span><span class="token keyword">char</span><span class="token operator">></span><span class="token punctuation">;</span>
<span class="token keyword">explicit</span> <span class="token function">Product</span><span class="token punctuation">(</span>allocator_type alloc <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token operator">:</span> _name<span class="token punctuation">{</span>alloc<span class="token punctuation">}</span>
<span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token function">Product</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>string name<span class="token punctuation">,</span> <span class="token keyword">char</span> price<span class="token punctuation">,</span>
<span class="token keyword">const</span> allocator_type<span class="token operator">&</span> alloc <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token operator">:</span> _name<span class="token punctuation">{</span>std<span class="token double-colon punctuation">::</span><span class="token function">move</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">,</span> alloc<span class="token punctuation">}</span><span class="token punctuation">,</span> _price<span class="token punctuation">{</span>price<span class="token punctuation">}</span>
<span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token function">Product</span><span class="token punctuation">(</span><span class="token keyword">const</span> Product<span class="token operator">&</span> other<span class="token punctuation">,</span> <span class="token keyword">const</span> allocator_type<span class="token operator">&</span> alloc<span class="token punctuation">)</span>
<span class="token operator">:</span> _name<span class="token punctuation">{</span>other<span class="token punctuation">.</span>_name<span class="token punctuation">,</span> alloc<span class="token punctuation">}</span><span class="token punctuation">,</span> _price<span class="token punctuation">{</span>other<span class="token punctuation">.</span>_price<span class="token punctuation">}</span>
<span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token function">Product</span><span class="token punctuation">(</span>Product<span class="token operator">&&</span> other<span class="token punctuation">,</span> <span class="token keyword">const</span> allocator_type<span class="token operator">&</span> alloc<span class="token punctuation">)</span>
<span class="token operator">:</span> _name<span class="token punctuation">{</span>std<span class="token double-colon punctuation">::</span><span class="token function">move</span><span class="token punctuation">(</span>other<span class="token punctuation">.</span>_name<span class="token punctuation">)</span><span class="token punctuation">,</span> alloc<span class="token punctuation">}</span><span class="token punctuation">,</span> _price<span class="token punctuation">{</span>other<span class="token punctuation">.</span>_price<span class="token punctuation">}</span>
<span class="token punctuation">{</span><span class="token punctuation">}</span>
Product<span class="token operator">&</span> <span class="token keyword">operator</span><span class="token operator">=</span><span class="token punctuation">(</span><span class="token keyword">const</span> Product<span class="token operator">&</span> other<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token punctuation">;</span>
Product<span class="token operator">&</span> <span class="token keyword">operator</span><span class="token operator">=</span><span class="token punctuation">(</span>Product<span class="token operator">&&</span> other<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>string _name<span class="token punctuation">;</span>
<span class="token keyword">char</span> _price<span class="token punctuation">{</span><span class="token char">'0'</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token keyword">int</span> argc<span class="token punctuation">,</span> <span class="token keyword">char</span><span class="token operator">*</span> argv<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">constexpr</span> size_t BUF_SIZE <span class="token operator">=</span> <span class="token number">256</span><span class="token punctuation">;</span>
<span class="token keyword">char</span> buffer<span class="token punctuation">[</span>BUF_SIZE<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span><span class="token function">fill_n</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span><span class="token function">begin</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">,</span> std<span class="token double-colon punctuation">::</span><span class="token function">size</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token char">'-'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token keyword">auto</span> BufferPrinter <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>string_view buf<span class="token punctuation">,</span>
std<span class="token double-colon punctuation">::</span>string_view title<span class="token punctuation">)</span> <span class="token punctuation">{</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> title <span class="token operator"><<</span> <span class="token string">":\n"</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span>size_t i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> buf<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token operator">++</span>i<span class="token punctuation">)</span> <span class="token punctuation">{</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token punctuation">(</span>buf<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">>=</span> <span class="token char">' '</span> <span class="token operator">?</span> buf<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">:</span> <span class="token char">'#'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>i <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">64</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token char">'\n'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
std<span class="token double-colon punctuation">::</span>cout <span class="token operator"><<</span> <span class="token char">'\n'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
debug_resource default_dbg<span class="token punctuation">{</span><span class="token string">"default"</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>monotonic_buffer_resource pool<span class="token punctuation">{</span>std<span class="token double-colon punctuation">::</span><span class="token function">data</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">,</span>
std<span class="token double-colon punctuation">::</span><span class="token function">size</span><span class="token punctuation">(</span>buffer<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">&</span>default_dbg<span class="token punctuation">}</span><span class="token punctuation">;</span>
debug_resource dbg<span class="token punctuation">{</span><span class="token string">"buffer"</span><span class="token punctuation">,</span> <span class="token operator">&</span>pool<span class="token punctuation">}</span><span class="token punctuation">;</span>
std<span class="token double-colon punctuation">::</span>pmr<span class="token double-colon punctuation">::</span>vector<span class="token operator"><</span>Product<span class="token operator">></span> products<span class="token punctuation">{</span><span class="token operator">&</span>dbg<span class="token punctuation">}</span><span class="token punctuation">;</span>
products<span class="token punctuation">.</span><span class="token function">reserve</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
products<span class="token punctuation">.</span><span class="token function">emplace_back</span><span class="token punctuation">(</span>Product<span class="token punctuation">{</span><span class="token string">"car"</span><span class="token punctuation">,</span> <span class="token char">'7'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
products<span class="token punctuation">.</span><span class="token function">emplace_back</span><span class="token punctuation">(</span>Product<span class="token punctuation">{</span><span class="token string">"TV"</span><span class="token punctuation">,</span> <span class="token char">'9'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
products<span class="token punctuation">.</span><span class="token function">emplace_back</span><span class="token punctuation">(</span>Product<span class="token punctuation">{</span><span class="token string">"a bit longer product name"</span><span class="token punctuation">,</span> <span class="token char">'4'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">BufferPrinter</span><span class="token punctuation">(</span>std<span class="token double-colon punctuation">::</span>string_view<span class="token punctuation">{</span>buffer<span class="token punctuation">,</span> BUF_SIZE<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token string">"after insertion"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>输出:</p>
<pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">buffer do_allocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">192</span>
buffer do_allocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">26</span>
after insertion:
<span class="token comment">#######(##############car#+## ###+##7-------#######X######</span>
<span class="token comment">########TV##+## ###+##9-------##############################</span>
--------4-------------------------------------------------------
a bit longer product name<span class="token comment">#-------------------------------------#</span>
buffer do_deallocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">26</span>
buffer do_deallocate<span class="token punctuation">(</span><span class="token punctuation">)</span>: <span class="token number">192</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>在输出中,第一个内存分配<code>192</code>字节用于<code>vector.reserve(3)</code>,然后有另一个用于更长的字符串(第三个元素)。还打印了完整的缓冲区,显示字符串所在的位置。</p>
<p>我们的自定义对象由其他<code>pmr</code>容器组成,因此更加简单!而且我想在大多数情况下您可以利用现有类型。但是,如果您需要访问分配器并执行自定义内存分配,那么您应该查看 Pablo 的演讲,他在其中介绍了自定义列表容器的示例。</p>
<p><a target="_blank" rel="noopener" href="https://www.youtube.com/watch?v=v3dz-AKOVL8">CppCon 2017: Pablo Halpern “Allocators: The Good Parts” - YouTube</a></p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>通过本文,我想向您展示一些有关pmr的基本示例和多态分配器的概念。如您所见,为<code>vector</code>设置分配器比使用常规分配器简单得多。您可以使用一组预定义的分配器,并且实现您的自定义版本相对容易。文章中的代码仅展示了一个简单的例子,以说明从何处获取内存。</p>
<p>在这篇博文中,我们在标准库的深层进行了另一次旅程。虽然分配器很可怕,但似乎多态分配器让事情变得更舒服。</p>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul>
<li><a target="_blank" rel="noopener" href="https://www.youtube.com/watch?v=v3dz-AKOVL8">CppCon 2017: Pablo Halpern “Allocators: The Good Parts” - YouTube</a> - in-depth explanations of allocators and the new PMR stuff. Even with a test implementation of some node-based container.</li>
<li><a target="_blank" rel="noopener" href="https://www.youtube.com/watch?v=LIb3L4vKZ7U">CppCon 2015: Andrei Alexandrescu “std::allocator…” - YouTube</a> - from the introduction you can learn than <code>std::allocator</code> was meant to fix far/near issues and make it consistent, but right now we want much more from this system.</li>
<li><a target="_blank" rel="noopener" href="https://stackoverflow.com/questions/4502691/what-is-the-purpose-of-allocator-traitst-in-c0x">c++ - What is the purpose of allocator_traits in C++0x? - Stack Overflow</a></li>
<li><a target="_blank" rel="noopener" href="https://jguegant.github.io/blogs/tech/dense-hash-map3.html#dense-hash-map3">Jean Guegant’s Blog – Making a STL-compatible hash map from scratch - Part 3 - The wonderful world of iterators and allocators</a> - this is a super detailed blog post on how to make more use of allocators, not to mention good anecdotes and jokes :)</li>
<li><a target="_blank" rel="noopener" href="https://blog.feabhas.com/2019/03/thanks-for-the-memory-allocator/">Thanks for the memory (allocator) - Sticky Bits</a> - a valuable introduction to allocators, their story and how the new model of PMR fit in. You can also see how to write your tracking pmr allocator and how <code>*_pool_resource</code> works.</li>
<li><a target="_blank" rel="noopener" href="https://www.youtube.com/watch?v=IejdKidUwIg">CppCon 2018: Arthur O’Dwyer “An Allocator is a Handle to a Heap”</a> - a great talk from Arthur where he shares all the knowledge needed to understand allocators.</li>
<li><a target="_blank" rel="noopener" href="https://amzn.to/3gsR3u9">C++17 - The Complete Guide</a> by Nicolai Josuttis - inside the book, there’s a long chapter about PMR allocators.</li>
</ul>
</div>
<hr/>
<div class="reprint" id="reprint-statement">
<div class="reprint__author">
<span class="reprint-meta" style="font-weight: bold;">
<i class="fas fa-user">
文章作者:
</i>
</span>
<span class="reprint-info">
<a href="https://nonguangxin.cn" rel="external nofollow noreferrer">农光鑫</a>
</span>
</div>
<div class="reprint__type">
<span class="reprint-meta" style="font-weight: bold;">
<i class="fas fa-link">
文章链接:
</i>
</span>
<span class="reprint-info">
<a href="https://nonguangxin.cn/C-%E5%A4%9A%E6%80%81%E5%88%86%E9%85%8D%E5%99%A8.html">https://nonguangxin.cn/C-%E5%A4%9A%E6%80%81%E5%88%86%E9%85%8D%E5%99%A8.html</a>
</span>
</div>
<div class="reprint__notice">
<span class="reprint-meta" style="font-weight: bold;">
<i class="fas fa-copyright">
版权声明:
</i>
</span>
<span class="reprint-info">
本博客所有文章除特別声明外,均采用
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY-NC-SA 4.0</a>
许可协议。转载请注明来源
<a href="https://nonguangxin.cn" target="_blank">农光鑫</a>
!
</span>
</div>
</div>
<script async defer>
document.addEventListener("copy", function (e) {
let toastHTML = '<span>复制成功,请遵循本文的转载规则</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">查看</a>';
M.toast({html: toastHTML})
});
function navToReprintStatement() {
$("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
}
</script>
<div class="tag_share" style="display: block;">
<div class="post-meta__tag-list" style="display: inline-block;">
<div class="article-tag">
<a href="/tags/%E5%A4%9A%E6%80%81%E5%88%86%E9%85%8D%E5%99%A8/">
<span class="chip bg-color">多态分配器</span>
</a>
<a href="/tags/%E5%86%85%E5%AD%98%E6%B1%A0/">
<span class="chip bg-color">内存池</span>
</a>
</div>
</div>
<div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
<link rel="stylesheet" type="text/css" href="/libs/share/css/share.min.css">
<div id="article-share">
<div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享!</p>"></div>
<script src="/libs/share/js/social-share.min.js"></script>