2016-08-17 6 views
4

Ich habe vor kurzem versucht zu verstehen, wie C++ - Zuweiser arbeiten, und ich habe auf die Implementierung der rot-schwarz-Struktur, die die STL-Bibliothek für Dinge wie std::set oder std::map verwendet , aber es gibt einige Dinge, die ich nicht verstehen kann.Zuweisungsverwendung in C++ (STL-Struktur)

Das erste, was tut, ist den allocator vom Typ konvertiert die Behälter zu speichern haben - _Val - auf die Art des Knotens, der Baum verwendet - _Rb_tree_node<_Val> - die rebind Vorlage:

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; 

Diese Ich kann es klären. Jetzt

, wenn ein Element eingesetzt ist, und es braucht einen neuen Knoten zu schaffen, was sie tut, ist dies

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

die ich zuordnet Raum für einen einzelnen Knoten übernehmen. Aber dann hat es diese

::new(__node) _Rb_tree_node<_Val>; 

, die ich nicht wirklich wissen, was es tut, da der Raum für __node bereits zugeteilt worden ist. Aber nach, dass es auch dieses tut

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

, die mich noch mehr verwirrt macht, weil angeblich ein Knoten konstruieren (ist der Knoten Allocator), aber es geht um den Zeiger __node->_M_valptr() die _Val* vom Typ ist.

Wenn jemand das erklären könnte, wäre ich sehr dankbar.

+0

Der 'Operator new' reserviert keinen Speicher, er erzeugt ein Objekt (und manchmal auch Speicher, aber nicht in Ihrem Fall). Also, ich würde sagen, die zweite Zeile (':: new (__ Knoten) _Rb_tree_node <_Val>;') wahrscheinlich baut den Knoten in der '__node' zugewiesen Speicherblock – alexeykuzmin0

+0

Ok, aber warum übergeben Sie den Zeiger' __node' an den Betreiber? Außerdem, wenn es den Knoten konstruiert, was macht das ':: construct()' danach? – gmardau

+0

"Warum den Zeiger übergeben?" Wie die Antworten erklären, ist dies die _Placement 'new'_-Syntax. Und wie sonst würde es wissen, wo das _new__ Objekt _place_? –

Antwort

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

Diese Form der new expression wird 'Platzierung neuer' genannt. Es weist keinen neuen Speicher zu, sondern nur ein Objekt in dem Speicherbereich, auf den das Argument zeigt. Hier ist __node ein Zeiger auf bereits zugewiesenen Speicher für den Knoten, dieser Ausdruck erstellt an dieser Stelle ein Objekt vom Typ _Rb_tree_node<_Val>.

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

diese Linie constructs ein Objekt vom Typ _Val in dem Speicher zu __node->_M_valptr() durch spitze.

+0

Danke. Nur das letzte. Warum verwendet es den Knotenzuordner für '_M_valptr()', der von einem anderen Typ ist? – gmardau

+0

@Mehlins was ist der Typ von '_M_valptr'? – user2296177

+0

'Val *'. Der Code lautet: '__gnu_cxx :: __ aligned_membuf <_Val> _M_storage; _Val * _M_valptr() {return _M_storage._M_ptr(); } ' – gmardau

2

Die Linie

::new(__node) _Rb_tree_node<_Val>; 

placement new verwendet, die einfach ein Objekt vom Typ __node)_Rb_tree_node<_Val> in gegebenen Speicheradresse-Konstrukte. Dies konstruiert das Knotenobjekt.

Jetzt muss es etwas mit einem der Mitglieder bei _M_valptr() tun. Die Linie

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

(indirekt Anrufe) der Zuordner des construct method, die zur globalen Platzierung sehr ähnlich ist new (in der Tat, es in der Regel nennt es einfach). Als solches nimmt es wieder einen Zeiger auf den Ort, an dem das Objekt konstruiert werden soll. Dies konstruiert das Wertobjekt.

+0

Ok, also nehme ich an, dass es das Placement 'new' verwendet, da es keinen zu übergebenden Wert hat (erforderlich in' :: construct() ') und dann die Funktion' :: construct() 'verwendet, weil es tatsächlich existiert ein Wert zu übergeben (versteckt in der '...'). Es gibt eine Sache, die ich immer noch nicht verstehe. Warum verwendet es den Knotenzuordner für '_M_valptr()', der von einem anderen Typ ist? – gmardau

+0

@Mehlins Dies hängt wirklich von der exakten Standardbibliothek * Implementierung * ab, die Sie verwenden, aber wiederum ist es fast positiv, die globale Platzierung nur indirekt als neu zu bezeichnen. Woher weiß es, über den Node Allocator den entsprechenden Aufruf zu machen? durch den von Ihnen erwähnten 'Rebind'-Mechanismus, durch Funktionsschablonen usw. Es wird nicht grundsätzlich der Knoten-Allokator verwendet, trotz des Erscheinens. –

2

Es wird etwas namens "Placement New" verwendet, mit dem ein Objekt im Speicher erstellt werden kann, der bereits zugewiesen wurde.

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

Oder man könnte es so schreiben:

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; 

Oder diese:

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! 

Die Grundidee ist, dass Sie sich jetzt für die manuelle Bereinigung verantwortlich werden normalerweise automatisch durch die behandelt Sprache und Compiler. Was ist extrem schlechte Praxis EXCEPT bei der Arbeit mit Zuweisern, wo direkte Kontrolle über die Speicherzuweisungen wird essentiell für das Schreiben eines guten Zuweisers.