2016-08-17 3 views
4

最近、C++アロケータの動作を理解しようとしており、STLライブラリがstd::setまたはstd::mapなどの目的で使用する赤黒のツリーの実装を検討しています。しかし、頭がおかしくないことがいくつかあります。C++でのアロケータの使用

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; 

これ:_Val - - - _Rb_tree_node<_Val>を - 再バインドテンプレートを使用してツリーが使用するノードのタイプを行い

まず、コンテナが格納しなければならないタイプのアロケータを変換であります私は整理することができます。要素が挿入されると、それはそれが何をするか、新しいノードを作成する必要がある

は、私は単一のノードに割り当てるスペースを想定し、この

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

です。 __nodeのためのスペースがすでに割り当てられているので、しかし、それは、私は本当にそれが何をするか分からない、この

::new(__node) _Rb_tree_node<_Val>; 

を行います。しかし、その後、それはまた、おそらくノードを構築していること(ノードアロケータである)ので、私はさらに混乱します。この

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

を行いますが、それはタイプ_Val*であるポインタ__node->_M_valptr()を渡します。

誰かがこれを説明できる場合、私は非常に感謝します。

+0

'operator new'はメモリを割り当てません。オブジェクトを作成します(場合によってはメモリも割り当てますが、あなたの場合は割り当てません)。だから、私は2番目の行( ':: new(__ node)_Rb_tree_node <_Val>;')はおそらく '__node'割り当てメモリブロックのノードを構築すると言っています – alexeykuzmin0

+0

しかし、なぜ' __node'ポインタを演算子に渡しますか?さらに、ノードを構築する場合、 ':: construct()'は後で何をしますか? – gmardau

+0

"なぜポインタを渡すのですか"?答えが説明するように、これは_placement 'new'_構文です。そして、それはどこに新しいオブジェクトをどこに置くべきかを知っていますか? –

答えて

6
::new(__node) _Rb_tree_node<_Val>; 

この形式のnew expressionは「プレースメント新」と呼ばれます。新しいメモリを割り当てるのではなく、引数で指定されたメモリ領域にオブジェクトを作成するだけです。ここで__nodeは、ノードにすでに割り当てられているメモリへのポインタです。この式は、この場所にタイプ_Rb_tree_node<_Val>のオブジェクトを構築します。

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

この行はメモリタイプ_Valのオブジェクトをconstructs__node->_M_valptr()によって指されます。

::new(__node) _Rb_tree_node<_Val>; 

+0

ありがとうございます。ただ最後のこと。 '_M_valptr()'にノードアロケータを使用するのはなぜですか?それは別の型ですか? – gmardau

+0

@Mehlins '_M_valptr'の型は何ですか? – user2296177

+0

'Val *'。コードは次のとおりです。 '__gnu_cxx :: __ aligned_membuf <_Val> _M_storage; _Val * _M_valptr(){return _M_storage._M_ptr(); } ' – gmardau

2

ラインは、単に与えられたメモリアドレス__node)でタイプ_Rb_tree_node<_Val>のオブジェクトを構築placement newを使用します。これにより、ノードオブジェクトが構築されます。

_M_valptr()のメンバーで何かをする必要があります。ライン

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

(間接的に呼び出し)グローバル配置new(実際には、それは一般的にそれを呼び出す)と非常によく似ているアロケータのconstruct method。このように、オブジェクトを構築する場所へのポインタを再度取る。これにより、値オブジェクトが構築されます。

+0

これは、 ':: construct()'で必要な)値を渡さないために 'new'というプレースメントを使っていると仮定しています。いくつかの値が渡されます( '...'で隠されています)。 私はまだ得られない1つのことがあります。 '_M_valptr()'にノードアロケータを使用するのはなぜですか?それは別の型ですか? – gmardau

+0

@Mehlinsこれは実際に使用している正確な標準ライブラリ*実装*に依存しますが、やはり間接的にグローバルプレースメントを新しく呼び出すことはほぼ確実です。どのようにノードアロケータを介して適切なものを呼び出すことを知っていますか?あなたが言及した 'rebind'メカニズムを介して、関数テンプレートなどを介して実行されます。apperanceにもかかわらず、それは基本的にnode allocatorを使用していません。 –

2

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); 

それとも、このようにそれを書くことができます:

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; 

またはこの:

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! 

基本的な考え方は、あなたが今通常は自動的で扱う手動でクリーンアップの責任になるということです言語とコンパイラ。アロケータを扱う際には、非常に悪い練習である例外があります。ここでは、メモリアロケーションの直接制御が良いアロケータの作成に不可欠になります。

関連する問題