堆内存 - 在 C++ 中使用 std::分配器而不是 new 有什么优势?
heap memory - What's the advantage of using std::allocator instead of new in C++?
我刚刚读到std::allocator
。在我看来,使用它比使用new
和delete
更复杂。
对于allocator
,我们必须显式地分配堆内存,构造它,销毁它,然后最后释放内存。那么它为什么被创造出来呢?
在什么情况下可以使用它,什么时候应该使用它而不是new和delete?
在我看来,使用它比使用new和delete更复杂。
是的,但它不是要取代new
和delete
,它有不同的目的。
对于allocator,我们必须显式地分配堆内存,构造它,销毁它,然后最后释放内存。
那么为什么要创建它呢?
因为有时你想把分配和构造分成两个步骤(类似地,把销毁和回收分成两个步骤)。如果您不想这样做,请不要使用分配器,而是使用new
。
在什么情况下可以使用它,什么时候应该使用它而不是new和delete?
当你需要一个分配器的行为,而不是new
和delete
的行为,显然!典型的情况是在实现容器时。
考虑以下代码:
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)必须销毁这些对象,但不释放内存。最后,在vector的析构函数中,所有的内存都可以被释放。
所以vector不能只使用new X()
或delete &m_data[1]
来创建和销毁对象,它必须单独执行分配/释放和构造/销毁。容器的allocator模板参数定义了应该用于(解)分配内存和构造/析构对象的策略,从而允许自定义容器对内存的使用。默认策略为std::allocator
。
因此,当需要分配器时(例如使用容器时)使用分配器,当不想提供自定义分配器而只想要标准分配器时使用std::allocator
。
您不使用分配器作为new
和delete
的替代品。
std::allocator
是标准库容器的默认内存分配器,您可以替换您自己的分配器。这允许您控制标准容器如何分配内存。但我不认为你的问题是关于std::allocator
的,而是关于分配内存的策略,然后在内存中构造对象,而不是使用new T[N]
,例如。
这样做的原因是new T[N]
不允许您控制调用哪些构造函数。它迫使你同时构造所有的对象。这对于std::vector
来说是非常糟糕的,例如,您只希望偶尔分配。
使用原始内存分配器,您可以分配一定数量的内存,这决定了您的容量。然后,当用户向vector中添加元素时(使用自己选择的构造函数),可以在该内存中相应构造对象。
然后当内存用完时,分配更多,通常是两倍。如果std::vector
使用new T[N]
,那么每次您想要添加或删除元素时,它都必须重新分配,这将对性能造成严重影响。您还将被迫对所有对象使用默认构造函数,这对std::vector
可以容纳的对象类型施加了不必要的限制。
分配器是STL中非常重要的概念。每个容器都可以将分配器作为参数。然后分配将使用这个分配器执行,而不是使用标准的分配器。
这是有用的,例如在池中分配相同大小的对象,以提高性能,或者如果有一个特殊的内存区域需要存储对象,则可能是必要的。
分配和构造的步骤是分开的,因为例如对于vector (std::vector::reserve
),重要的是能够分配内存以供将来使用,而不是(尚未)在其中创建对象。
作为一个例子,你可以写一个分配器作为一个类,包含一个固定大小的数组,并使用该数组为一些标准容器提供内存。然后,您可以在堆栈上拥有该类的实例,从而完全避免程序的某些部分的堆分配。
在这篇文章中看到更多的例子。
[…[…]
当您有特定的需求时,最重要的是在编写自己的泛型容器时。
创建std::allocator
是为了允许开发人员更好地控制内存的分配方式。在许多嵌入式系统中,内存是受限的,并且是不同类型的。数量可能不会很大。此外,内存分配要最小化,以避免碎片问题。
分配器还允许从不同的内存池进行分配。例如,从小块内存池中分配小块会更有效。
你的直觉是对的。在90%的情况下,使用new
。但是,请注意像map数据结构这样的结构。它的默认模板参数之一是class Alloc = allocator<pair<const Key,T>
,它定义了类如何创建事物的新实例和管理现有实例。通过这种方式,理论上可以创建自己的分配器,然后将其用于现有的数据结构。由于new
和delete
是函数而不是类,因此有必要使用std::allocator
来表示它们并使它们成为有效的模板参数。
new
和delete
是在动态内存中创建对象并初始化它的直接方法。然而,分配器的作用要大得多,因为它们提供了对上述阶段的完全控制。
对于allocator,我们必须显式地分配堆内存,构造它,销毁它,然后最后释放内存。
实际上,分配器不应该用于"正常"代码,其中new
和delete
同样可以。考虑一个像std::map
这样的类,它通常被实现为一个树:当一个持有的对象被删除时,你需要释放整个叶子吗?分配器允许您销毁该对象,但保留内存,以便您不必再次要求它。
更进一步,你可以专门化一个特定类型的分配器,如果你知道更多的优化方法来控制它的new
和delete
是不可能的。
你困惑了。std::allocator
调用/使用new
和delete
。它只是c++内存层次结构中的另一个层次,用于满足c++标准库的各种需求,特别是容器,但也有其他类型。c++库容器使用分配器自动管理所包含元素的内存。没有它,事情会变得更麻烦,因此更难以使用。此外,分配器可以用于执行不同的内存管理技术,例如堆栈分配,线性分配,堆分配,池分配等。
c++内存"hierarchy"
_________________
|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。你看,这都是一个抽象概念。上面的级别抽象了一个更困难的特性,并将其包装在一个更容易使用的包中。
使用这个STL成员的原因是为了给开发人员更多的内存控制。我的意思是,例如,new运算符本身不仅仅是一个运算符。在最基本的情况下,它执行内存预留,然后用对象填充该空间。
虽然我不能马上想出一个具体的、真实的场景,但是当一个给定对象的销毁可能会影响内存中的其他对象时,您应该使用std::allocator
等。
假设,为了便于讨论,你创建了某种向量,它的每个元素都与内存中的其他对象进行了双链接,并且你希望,在删除该向量时,链接的对象删除对它的引用
- 如果我在 c++ 中以 new 的放置形式使用没有足够的内存,会发生什么情况?
- int** a = new int*[n]();这个函数有什么作用?
- "new int * **[10]"做什么?
- 有些时候,阶级必须"default copy c'tor" ?当我做"new Type[..]"时会发生什么?
- 对于没有功能的结构,"new (ptr) mystruct;"做什么?
- 使用泛型成员变量"placement new"结构/类数组的正确方法是什么?
- new和::new有什么区别
- 带"new"的指针和带"&variable"的指针有什么区别
- "a = new arr ***[num];"的含义是什么?
- 将内存分配给 2D 数组时,“new int*[rowCount];”的含义是什么?是 2D 数组,是指向数组的指针数组
- C++用法:" *(new int); "做什么?
- new _Elem[_Size]() 与 new _Elem[_Size]{} 有什么区别?
- int '*p = new int (5);' 和 'int *p = new int[5];' 有什么区别?
- (类 *对象;)和(object = new class();)之间有什么区别
- 除了使用 new 之外,什么会导致内存泄漏?(C++)
- "{}" "new int[5]{};"的目的是什么?
- 新的int()和new int []有什么区别
- "new Classname*[]"是什么意思?
- "new int(100)"做什么?
- 在混合 C/C++ 程序中协调 malloc 和 new 的"正确"方法是什么?