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


73

我刚刚读过有关std::allocator。我认为,使用它而不是使用newand更复杂delete

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

在什么情况下可以使用它,何时应使用它代替new和delete?


12
问题不是关于malloc而是std::allocator
Silvio Mayolo

4
@EvanCarslake以及如何newdelete未分配/释放内存?真正的区别是完全无关的:构造函数/析构函数的执行。
deviantfan


Answers:


55

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

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

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

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


我认为s/allocate/construct“您只希望偶尔分配的地方”中
Nawaz 2015年

@Nawaz:好吧,我都猜到了。但我确实的确是在那儿分配。毕竟,您可以使用来实现vector(非常愚蠢)new T[size]。然后,每次添加元素时,都将使用进行重新分配new T[++size]。这就是(我认为)问题所在:使用new T[N]vs将分配与构造分离。
本杰明·林德利

如果您阅读“并且它迫使您同时构造所有对象。这太糟糕了……。”,然后是“您只希望偶尔分配的位置”。。第二句话与前一句话的推论不太吻合。至少,这就是我的感觉(并且仍然感觉到)。
纳瓦兹

64

我认为,使用它而不是使用new和delete更为复杂。

是的,但这并不意味着要替换newdelete,它具有不同的用途。

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

那么为什么创建它呢?

因为有时您希望将分配和构造分为两个步骤(并且类似地将销毁和重新分配分为两个步骤)。如果您不想这样做,请不要使用分配器,new而应使用。

在什么情况下可以使用它,何时应使用它代替new和delete?

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

考虑以下代码:

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

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

因此,向量不能仅使用new X()delete &m_data[1]创建和销毁对象,它必须与构造/销毁分开执行分配/重新分配。容器的分配器模板参数定义了应用于(取消)分配内存和构造/销毁对象的策略,从而可以自定义容器的内存使用。默认策略是std::allocator类型。

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

您不使用分配器代替newdelete


10

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

这很有用,例如用于在池中分配相同大小的对象,以提高性能,或者在有需要对象驻留的特殊内存区域时可能是必要的。

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

作为一个例子可以编写一个分配器为一类,包含固定尺寸数组,并使用该阵列,以对于一些标准集装箱提供存储器。然后,您可以在堆栈上拥有该类的实例,从而完全避免为程序的某些部分分配堆。

在此SO帖子中查看更多示例。

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

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


7

std::allocator是为了让开发人员的内存是如何分配的更多的控制。在许多嵌入式系统中,内存受约束且类型不同。可能数量不多。另外,要尽量减少内存分配以避免碎片问题。

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


1
实际上,分配器已添加到STL以封装内存模型的详细信息,例如,带有“近”和“远”指针的分段内存系统(例如早期的Intel x86芯片),不允许自定义内存分配方式。参见sgi.com/tech/stl/drdobbs-interview.htmlstlport.org/resources/StepanovUSA.html
Jonathan Wakely

即使是某些相对较新的系统,例如Playstation 3,也有必要使用自定义分配器,以将您的数据结构映射到内存的特定区域。更不用说想要在例如mmap()区域或IPC缓冲器等中分配的情况。
蓬松的

6

你的直觉是对的。在90%的情况下,请使用new。但是,请注意诸如地图数据结构之类的结构。它的默认模板参数之一是class Alloc = allocator<pair<const Key,T>,它定义类如何创建事物的新实例并管理现有实例。这样,您理论上可以创建自己的分配器,然后将其用于现有数据结构。由于newdelete是函数而不是类,因此必须具有std::allocator来表示它们并使它们成为有效的模板参数。


4

new并且delete是在动态内存中创建对象并对其进行初始化的直接方法。分配器的功能更多,因为它们可以完全控制上述阶段。

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

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

此外,如果您知道用于它的控制的更多优化方法(对于newand不可能),则可以专门针对某种类型的分配器delete


4

STL成员的原因是使开发人员可以更好地控制内存。我的意思是,例如,新运算符本身实际上并不是一个操作。最基本的做法是保留内存,然后用对象填充该空间。

尽管我无法直截了当地提出一个特定的实际案例,但是您应该使用std::allocator诸如此类的方法,例如,某个给定对象的破坏可能会影响内存中的其他对象。

假设出于争论的目的,您创建了一种向量,该向量的每个元素都与内存中的其他对象双链接,并且您希望在删除该向量时链接的对象将引用删除回它。


2

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

C ++内存“层次结构”

_________________
|Applications   |
|_______________|
      |
______↓_______________________
|C++ library (std::allocator)|
|____________________________|
      |
______↓______________________________________________________________________________
|C++ primitives (new/delete, new[]/delete[], ::operator new()/::operator delete())  |
|___________________________________________________________________________________|
      |
______↓______
|malloc/free|
|___________|
      |
______↓______________
|OS APIs, syscalls  |
|___________________|

这是正常的调用流程,但是应用程序可以直接调用malloc / free或new / delete甚至OS API。您会看到所有内容都是抽象的。上面的级别抽象了一个较难的特性,并将其包装在易于使用的程序包中。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.