2010-11-22 11 views
8

Ich komme aus Java in C++ und habe eine gemeinsame Design-Situation, in der ich ein Element (ein nicht-primitives) habe, das ich aus einem std :: vector entfernen möchte.ArrayList-style indexOf für std :: Vektor in C++?

in Java würde ich etwas wie schreiben: arrayList.remove (arrayList.indexOf (myClassInstance));

in C++, mit einem std :: vector, was ist die beste/leistungsfähigste/sauberste Art, dies zu tun?

Das Beste, was ich mir vorstellen kann, ist, einen Verweis auf die Instanz zu erstellen, nach der ich suche, und dann durch den Vektor zu iterieren, bis ich diese Referenz finde. im Wesentlichen, um die Speicheradresse jedes Elements in dem Vektor mit der Referenz zu vergleichen, bis ich eine Übereinstimmung erhalte.

Bin ich auf dem richtigen Weg? oder gibt es einen besseren Weg? (vielleicht mit einem anderen Std-Container, ich habe bisher nur std :: vector verwendet.)

+0

Sie haben eine Sammlung von Zeigern oder Shared_ptr Unter der Annahme, std :: Set kann auch für Sie arbeiten , nur die Zeigeradressen zu vergleichen. Wenn Sie die Adresse des Artikels kennen, nach dem Sie suchen, nur mySet.löschen (ptr); – CashCow

+0

@CashCow - gibt es einen großen Leistungsunterschied bei der Iteration über alle Member eines std :: set gegenüber einem std: vector? An anderer Stelle in meinem Code rufe ich für jedes Element in jedem Zyklus eine Methode auf. – ericsoco

Antwort

8
#include <algorithm> 

std::vector<Foo>::iterator it = std::find(vec.begin(), vec.end(), foo_2b_found); 
if (it != vec.end()) vec.erase(it); 
+1

Ich denke, du verpasst am Ende dieses 'std :: find'-Anrufs ein paar Sachen :) –

+1

ist es nicht eine schlechte Idee, beim Iterieren zu löschen? oder ich denke, keine schlechte Idee hier, denn wenn wir Vector.Erase aufrufen, sind wir fertig mit dem Iterator und es spielt keine Rolle mehr, ob es ungültig ist. – ericsoco

+0

@Billy: Danke, dass du das gefunden hast :) @eric: Wenn wir * manuell * iterieren würden, müssten wir sehr vorsichtig sein (aber wir tun es nicht). Iterator Invalidation ist ein sehr interessantes Thema. Rieche ich eine andere FAQ? ;-) – fredoverflow

4

Verwenden Sie std::find, um das Element zu finden und vector::erase, um es zu entfernen.

std::find iteriert im Wesentlichen durch den Vektor, um das Element zu finden, und Sie können nicht besser mit einem einfachen Vektor (das gleiche ist der Fall mit Javas ArrayList). Ob Sie einen anderen Container verwenden oder nicht, hängt von Ihren Anforderungen ab.

+0

+1. Beachten Sie, dass Sie, wenn Sie mehrere Elemente entfernen, die mit einem Prädikat übereinstimmen, auch 'std :: remove_if' verwenden sollten. –

+0

oo. merkte nicht, dass es so viele Dinge gab, die man mit Std-Containern machen konnte ... - http://www.cplusplus.com/reference/algorithm/ – ericsoco

+0

@eric: Willkommen in der wunderbaren Welt der generischen Programmierung! – fredoverflow

1

Wenn Sie möchten, linear durch den Vektor suchen dann

seq.erase(std::find(seq.begin(), seq.end(), elt)); 

Wenn Sie ein Prädikat und wollen alle Elemente entfernen, die das Prädikat entsprechen dann:

seq.erase(std::remove_if(seq.begin(), seq.end(), Pred), seq.end()); 

Keines Diese Methoden sind am leistungsfähigsten, da sie eine lineare Suche erfordern. Selbst wenn Ihr Element zu einem frühen Zeitpunkt gefunden wird, ist das Löschen teuer, da alle anderen Elemente durch eine Position verschoben werden müssen, um sie zusammenhängend zu halten s.

Die Verwendung von std :: list würde letzteres adressieren: Die Suche wäre linear, aber das Löschen wäre eine konstante Zeit.

Wenn es möglich ist, Ihre Elemente in einem assoziativen Container zu speichern, der eine Schlüsselsuche verwendet, wäre das effizienter: O (log N) Lookup und konstante Zeitentfernung.

Eine Hash-Karte kann sogar besser sein, in der Nähe von konstanter Zeit suchen und entfernen.

Für was Sie vorschlagen, d. H. Löschen durch den Zeiger des Objekts, könnten Sie std :: set für Ihren Typ T verwenden. Dann verwenden Sie mySet.erase(pt); wo pt ist Ihr Zeiger. Sie müssen natürlich die Lebensdauer Ihrer Zeiger verwalten, aber die Tatsache, dass Sie wissen, welche Sie aus Ihrer Sammlung löschen möchten, legt nahe, dass Sie eine Kopie davon noch woanders haben.

Sie könnten std :: set verwenden, SharedPtrLess>

wo Sie SharedPtrLess wie folgt definieren:

template< typename T > 
struct SharedPtrLess 
{ 
    bool operator()(boost::shared_ptr<T> left, boost::shared_ptr<T> right) const 
    { 
    return std::less<T>()(left.get(), right.get()); 
    } 
}; 
+0

tolle Tipps für die Zukunft. ich werde anfangen, indem ich std :: vector unter meinen gürtel nehme und von dort weitergehe ... danke! – ericsoco