2014-04-22 10 views
5

Ich verstehe, wie Random-Access-Iteratoren für zusammenhängende Container wie Std :: Vector arbeiten: der Iterator verwaltet einfach einen Zeiger auf das aktuelle Element und alle Additionen/Subtraktionen werden auf den Zeiger angewendet. Ich bin jedoch verblüfft darüber, wie ähnliche Funktionalität für einen nicht zusammenhängenden Container implementiert werden könnte. Meine erste Vermutung für, wie std :: deque: Iterator funktioniert ist, dass es einen Zeiger auf eine Tabelle der Gruppen von zusammenhängenden Speicher verwaltet, die es enthält, aber ich bin mir nicht sicher.Wie werden Direktzugriffs-Iteratoren für nicht zusammenhängende Container (z. B. std :: deque) implementiert?

Wie würde eine typische Standardbibliothek dies implementieren?

+0

Wer sagt, dass ein 'Deque' nicht zusammenhängend ist? Es wird normalerweise als dynamisches Array implementiert. – ooga

+2

@ooga von [hier] (http://en.cppreference.com/w/cpp/container/deque) 'Im Gegensatz zu std :: vector werden die Elemente einer Deque nicht zusammenhängend gespeichert: typische Implementierungen verwenden eine Sequenz von individuell zugewiesenen Arrays fester Größe. –

+1

@ooga, Wie würde es dann von einem Vektor abweichen? – chris

Antwort

1

Sie können die Anforderungen einer std::deque mit einer std::vector<std::unique_ptr<std::array<T,N>>> grob erfüllen. plus eine untere/obere Wassermarke, die anzeigt, wo sich die ersten/letzten Elemente befinden. (für eine Implementierung definiert N, die mit T variieren könnte, und die s sind tatsächlich Blöcke von ordnungsgemäß ausgerichtet nicht initialisierten Speicher und nicht s, aber Sie bekommen die Idee).

Verwenden Sie das übliche exponentielle Wachstum, aber sowohl auf der Vorder- als auch auf der Rückseite.

Suche einfach (index+first)/N und %N, um das Block- und Unterelement zu finden.

Dies ist teurer als ein std::vector Lookup, aber ist O (1).

+0

Bei [http://cppreference.com] (http: // cppreference.com) Die Seite für deque :: push_back zeigt eine konstante Zeitkomplexität an, während die Seite für vector :: push_back eine amortisierte konstante Zeitkomplexität anzeigt. Würde die Verwendung eines Vektors als Backend für Zeiger auf Arrays die Anforderung, dass deque :: push_back konstant ist, nicht verletzen? Oder ist amortisierte Konstante akzeptabel? – chbaker0

+0

@ Mebob woanders heißt es amortisierte Konstante: ist ein Fehler. Ich sollte es nach der Bestätigung mit dem Standard beheben. – Yakk

+0

OK, das macht Sinn. Ich kann mir nicht wirklich einen Weg vorstellen, um die amortisierte konstante Grenze zu umgehen. Und zurück zu meiner Frage über Iteratoren: also würde ein Iterator einfach einen Verweis auf den besagten Vektor halten und die Puffer wechseln, wenn er das Ende von einem erreicht? – chbaker0

0

Ein Deque-Iterator kann implementiert werden, indem sowohl ein Zeiger auf den referenzierten Wert als auch ein Doppelzeiger auf den zusammenhängenden Speicherblock gespeichert wird, in dem sich dieser Wert befindet. Der Doppelzeiger zeigt auf ein zusammenhängendes Array von Zeigern auf Blöcke, die von der Deque verwaltet werden.

class deque_iterator 
{ 
    T* value; 
    T** block; 
    … 
} 

sowohl Da value und block Punkt in zusammenhängenden Speicher, können Sie Operationen wie das Finden der Abstand zwischen Iteratoren in konstanter Zeit (zB angepasst von libC++) implementieren.

difference_type operator-(deque_iterator const& x, deque_iterator const& y) 
{ 
    return (x.block - y.block) * block_size 
     + (x.value - *x.block) 
     - (y.value - *y.block); 
} 

Beachten Sie, dass, während value nicht durch Operationen wie push_front und push_back, für ungültig erklärt werden block sein könnte, weshalb deque_iterator durch solche Operationen für ungültig erklärt wird.