在C++中使用std :: allocator而不是new有什么好处?

Mug*_*rel 63 c++ heap alloc

我刚看过std::allocator.在我看来,使用它而不是使用new和更复杂delete.

随着allocator我们必须明确分配堆内存,构建它,消灭它,然后终于解除分配内存.那它为什么会被创造出来?

在哪些情况下可以使用它何时应该使用而不是新的和删除?

Jon*_*ely 59

在我看来,使用它而不是使用new和delete更复杂.

是的,但它并不意味着取代newdelete,它用于不同的用途.

使用allocator,我们必须显式分配堆内存,构造它,销毁它,然后最终释放内存.

那它为什么会被创造出来?

因为有时你想将分配和构造分成两个步骤(类似地将破坏和释放分成两个步骤).如果您不想这样做,请不要使用分配器,new而是使用.

在哪些情况下可以使用它何时应该使用而不是新的和删除?

当你需要一个分配器的行为,而不是行为newdelete,很明显!典型的情况是实现容器.

请考虑以下代码:

std::vector<X> v;
v.reserve(4);        // (1)
v.push_back( X{} );  // (2)
v.push_back( X{} );  // (3)
v.clear();           // (4)
Run Code Online (Sandbox Code Playgroud)

这里第(1)行必须为四个对象分配足够的内存,但还没有构造它们.然后第(2)和(3)行必须将对象构造到已分配的内存中.然后第(4)行必须销毁这些对象,但不能解除内存的释放.最后,在向量的析构函数中,可以释放所有内存.

因此,向量不能仅使用new X()delete &m_data[1]创建和销毁对象,它必须与构造/销毁分开执行分配/释放.容器的allocator模板参数定义了应该用于(de)分配内存和构造/破坏对象的策略,允许容器使用内存进行自定义.默认策略是std::allocator类型.

因此,当需要分配器时(例如使用容器时)使用std::allocator分配器,而当您不想提供自定义分配器并且只想要标准分配器时使用分配器.

不要使用一个allocator作为替代newdelete.


Ben*_*ley 48

std::allocator是标准库容器的默认内存分配器,您可以替换自己的分配器.这允许您控制标准容器如何分配内存.但我不认为你的问题是std::allocator专门的,而是分配内存的策略,然后在内存中构建对象,而不是使用new T[N],例如.

原因是new T[N]不允许您控制调用哪些构造函数.它迫使你同时构建所有对象.例如,std::vector在您只想偶尔分配的情况下,这很糟糕.

使用原始内存分配器,您可以分配一定量的内存,从而确定容量.然后,当用户将项添加到向量(使用他们选择的构造函数)时,您可以在此内存中构造对象.

然后当你的内存不足时,你会分配更多,通常是两倍.如果std::vector使用new T[N],则每次要添加或删除元素时都必须重新分配,这对性能来说太糟糕了.您还将被迫对所有对象使用默认构造函数,这会对std::vector可容纳的对象类型施加不必要的限制.


Dan*_*our 9

分配器是STL中非常重要的概念.每个容器都能够将分配器作为参数.然后将使用此分配器执行分配,而不是标准分配器.

这对于在池中分配相同大小的对象,提高性能,或者如果您的对象需要存在特殊的内存区域可能是必要的.

分配和构造的步骤是分开的,因为例如对于vector(std::vector::reserve)来说,重要的是能够分配存储器以供将来使用,但是(还)不在其中创建对象.

作为示例,您可以将分配器编写为包含固定大小数组的类,并使用该数组为某个标准容器提供内存.然后,您可以在堆栈上拥有该类的实例,从而完全避免程序的某些部分的堆分配.

请参阅此SO帖子中的更多示例.

[...]什么时候应该使用[...]

当您有特定需求时,最重要的是在编写自己的通用容器时.


Tho*_*ews 6

std::allocator创建它是为了让开发人员能够更好地控制内存的分配方式.在许多嵌入式系统中,存储器受到约束并且具有不同的类型.可能没有太大的数额.此外,希望最小化内存分配以避免碎片问题.

分配器还允许从不同的内存池进行分配.因此,例如,从小块内存池中分配小尺寸块将更有效.


Sil*_*olo 6

你的直觉是对的.在90%的情况下,使用new.但是,请注意结构,例如地图数据结构.它的一个默认模板参数是class Alloc = allocator<pair<const Key,T>,它定义了类如何创建事物的新实例并管理现有实例.通过这种方式,理论上您可以创建自己的分配器,然后将其用于现有数据结构.由于newdelete是功能,而不是类,就必须有std::allocator代表他们,让他们有效的模板参数.


edm*_*dmz 5

new并且delete是建立在动态内存对象并初始化它的直接方式。分配器更多,因为它们提供对上述阶段的完全控制。

使用分配器,我们必须显式分配堆内存,构造它,销毁它,最后释放内存。

实际上,分配器不应该用于“正常”代码,new并且delete同样可以。考虑一个类似 的类std::map,它通常被实现为一棵树:每当一个对象被删除时,你是否需要释放整个叶子?分配器允许您销毁该对象,但保留内存以便您不必再次需要它。

此外,如果您知道更优化的控制方法,这对于new和是不可能的,那么您可以为某种类型专门化一个分配器delete


Nik*_*kos 5

你很困惑。std::allocator调用/使用newdelete. 它只是 C++ 内存层次结构中的另一个级别,用于满足 C++ 标准库的各种需求,特别是容器,但也满足其他类型。C++ 库容器使用分配器自动管理所包含元素的内存。如果没有它,事情就会变得更加麻烦,从而更加难以使用。此外,分配器可用于执行不同的内存管理技术,例如堆栈分配、线性分配、堆分配、池分配等。

\n\n

C++内存“层次结构”

\n\n
_________________\n|Applications   |\n|_______________|\n      |\n______\xe2\x86\x93_______________________\n|C++ library (std::allocator)|\n|____________________________|\n      |\n______\xe2\x86\x93______________________________________________________________________________\n|C++ primitives (new/delete, new[]/delete[], ::operator new()/::operator delete())  |\n|___________________________________________________________________________________|\n      |\n______\xe2\x86\x93______\n|malloc/free|\n|___________|\n      |\n______\xe2\x86\x93______________\n|OS APIs, syscalls  |\n|___________________|\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是正常的调用流程,但应用程序可以直接调用 malloc/free、new/delete 甚至操作系统 API。你看,这都是一个抽象概念。上面的级别抽象了更困难的性质,并将其包装在一个更易于使用的包中。

\n