简体   繁体   English

C ++中的分配器用法(STL树)

[英]Allocator usage in C++ (STL Tree)

I've recently been trying to understand how c++ allocators work, and I've been looking to the implementation of the red-black tree that the STL library uses for things like std::set or std::map , but there are some things that I can't get my head around. 我最近一直试图理解c ++分配器是如何工作的,我一直在寻找STL库用于std::setstd::map类的红黑树的实现,但是有一些我无法理解的事情。

The first thing that does is convert the allocator from the type the container has to store - _Val - to the type of the node that the tree uses - _Rb_tree_node<_Val> - using the rebind template: 首先要做的是将分配器从容器必须存储的类型 - _Val - 转换为树使用的节点类型 - _Rb_tree_node<_Val> - 使用重新绑定模板:

typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
    rebind<_Rb_tree_node<_Val> >::other _Node_allocator;

typedef __gnu_cxx::__alloc_traits<_Node_allocator> _Alloc_traits;

This I can sort out. 我可以解决这个问题。

Now, when an element is inserted and it needs to create a new node what it does is this 现在,当插入一个元素并且需要创建一个新节点时,它的作用就是这个

_Node_type __node = _Alloc_traits::allocate(_M_get_Node_allocator(), 1);

which I assume allocates space for a single node. 我假设为单个节点分配空间。 But then it does this 但是它就是这样做的

::new(__node) _Rb_tree_node<_Val>;

which I really don't know what it does, since the space for __node has already been allocated. 我真的不知道它做了什么,因为__node的空间已经被分配了。 But after that it also does this 但在此之后它也会这样做

_Alloc_traits::construct(_M_get_Node_allocator(), __node->_M_valptr(), ...);

which makes me even more confused, because is supposedly constructing a node (is the node allocator), but it passes the pointer __node->_M_valptr() which is of type _Val* . 这让我更加困惑,因为它应该构建一个节点(是节点分配器),但它传递指针__node->_M_valptr() ,它是_Val*类型。

If someone could explain this, I would be very grateful. 如果有人能够解释这一点,我将非常感激。

::new(__node) _Rb_tree_node<_Val>;

This form of new expression is called 'placement new'. 这种新表达形式称为“放置新”。 It does not allocate new memory, but only constructs an object in the memory region pointed by the argument. 它不分配新内存,而只构造参数指向的内存区域中的对象。 Here __node is a pointer to already allocated memory for the node, this expression constructs an object of type _Rb_tree_node<_Val> in this place. 这里__node是指向已经为节点分配的内存的指针,该表达式在这个地方构造一个_Rb_tree_node<_Val>类型的对象。

_Alloc_traits::construct(_M_get_Node_allocator(), __node->_M_valptr(), ...);

this line constructs an object of type _Val in the memory pointed to by __node->_M_valptr() . 此行在__node->_M_valptr()指向的内存中构造 _Val类型的对象。

The line 这条线

::new(__node) _Rb_tree_node<_Val>;

uses placement new , which simply constructs an object of type _Rb_tree_node<_Val> at given memory address __node) . 使用placement new ,它只是在给定的内存地址__node)构造一个_Rb_tree_node<_Val>类型的对象。 This constructs the node object. 这构造了节点对象。

Now it needs to do something with one of the members at _M_valptr() . 现在它需要与_M_valptr()一个成员做一些事情。 The line 这条线

_Alloc_traits::construct(_M_get_Node_allocator(), __node->_M_valptr(), ...);

(indirectly calls) the allocator's construct method which is very similar to the global placement new (in fact, it typically just calls it). (间接调用)分配器的construct方法 ,它非常类似于全局布局new (事实上​​,它通常只是调用它)。 As such, it again takes a pointer to the location where to construct the object. 因此,它再次获取指向构造对象的位置的指针。 This constructs the value object. 这构造了值对象。

It's using something called " Placement New ", which allows an object to be constructed in memory that has already been allocated. 它使用了一种名为“ Placement New ”的东西,它允许在已经分配的内存中构造一个对象。

void * mem = malloc(sizeof(MyStruct));
MyStruct * my_struct_ptr = new(mem) MyStruct(/*Args...*/);

/*Do stuff...*/

my_struct_ptr->~MyStruct();//Literally one of the only times a good programmer would ever do this!
free(mem);

Or you could write it like this: 或者你可以像这样写:

char * mem = new char[sizeof(MyStruct)];
MyStruct * my_struct_ptr = new(mem) MyStruct(/*Args...*/);

/*Do stuff...*/

my_struct_ptr->~MyStruct();//Literally one of the only times a good programmer would ever do this!
delete mem;

Or this: 或这个:

char mem[sizeof(MyStruct)];
MyStruct * my_struct_ptr = new(mem) MyStruct(/*Args...*/);

/*Do stuff...*/

my_struct_ptr->~MyStruct();//Literally one of the only times a good programmer would ever do this!

The basic idea is that you now become responsible for the manual cleanup normally automatically handled by the language and compiler. 基本思想是,您现在负责通常由语言和编译器自动处理的手动清理。 Which is extremely bad practice EXCEPT when working with allocators, where direct control over the memory allocations becomes essential to writing a good allocator. 在使用分配器时, 除了内存分配的直接控制对于编写好的分配器至关重要,这是非常糟糕的做法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM