2010-05-03 7 views
9

Quick Hintergrund
Ich bin ein Java-Entwickler, der in meiner freien/gelangweilten Zeit herum mit C++ gespielt hat.Warum sollte pop() ein Argument nehmen?

Vorwort
In C++, Sie oft pop sehen ein Argument durch Verweis unter:

void pop(Item& removed); 

Ich verstehe, dass es schön ist der Parameter mit auf "ausfüllen", was Sie entfernt. Das macht total Sinn für mich. Auf diese Weise kann sich die Person, die das oberste Element entfernen möchte, ansehen, was entfernt wurde.

Allerdings, wenn ich dies in Java tun, würde ich so etwas tun:

Item pop() throws StackException; 

diese Weise nach dem Knall wir entweder zurück: NULL als Ergebnis eines Artikel oder einem Ausnahme würde ausgelöst werden.

Mein C++ - Lehrbuch zeigt mir das obige Beispiel, aber ich sehe viele Stapelimplementierungen, die keine Argumente annehmen (stl stack zum Beispiel).

Die Frage
Wie man die Pop-Funktion in C++ implementieren sollte?

Der Bonus
Warum?

Antwort

26

Um die Frage zu beantworten: Sie sollten nicht die Pop-Funktion in C++ implementieren, da es bereits von der STL implementiert ist. Der Containeradapter bietet die Methode top, um eine Referenz auf das oberste Element im Stapel zu erhalten, und die Methode pop, um das oberste Element zu entfernen. Beachten Sie, dass die Methode pop allein nicht zum Ausführen beider Aktionen verwendet werden kann, wie Sie gefragt haben.

Warum sollte es so gemacht werden?

  1. Ausnahme Sicherheit: Herb Sutter gibt eine gute Erklärung für die Ausgabe in GotW #82.
  2. Single-Verantwortung-Prinzip: auch in GotW # 82 erwähnt. top kümmert sich um eine Verantwortung und pop kümmert sich um die andere.
  3. Zahlen Sie nicht für das, was Sie nicht brauchen: Für einige Code kann es ausreichen, das oberste Element zu untersuchen und dann zu öffnen, ohne jemals eine (möglicherweise teure) Kopie des Elements zu erstellen. (Dies ist in der SGI STL Dokumentation erwähnt.)

Jeder Code, der eine Kopie des Elements erhalten möchte, kann dies ohne zusätzliche Kosten tun:

Foo f(s.top()); 
s.pop(); 

Auch this discussion interessant sein kann.

Wenn Sie Pop implementieren wollten, um den Wert zurückzugeben, spielt es keine Rolle, ob Sie als Wert zurückgeben oder in einen out-Parameter schreiben.Die meisten Compiler implementieren RVO, was die Rückgabewertmethode so optimiert, dass sie genauso effizient ist wie die Copy-in-Out-Parametermethode. Denken Sie daran, dass beides wahrscheinlich weniger effizient ist, als das Objekt mit top() oder front() zu untersuchen, da in diesem Fall absolut kein Kopieren erfolgt.

+0

Awesome Links, vielen Dank. Also, wenn ich in einem Interview gebeten werde, pop() in C++ zu implementieren ... sollte ich ihnen Ihre Antwort geben? : p – Stephano

+2

+ 1 ... aber vielleicht "wenn Sie die Pop-Funktion in C++ implementieren, sollten Sie die Standard-Container-Schnittstelle folgen." – Potatoswatter

+1

@Stephano: Hängt davon ab, was der Interviewer will. Einige können davon überzeugt sein, dass Sie über 'std :: stack 'Bescheid wissen und wissen, dass es' push', 'top' und' pop' Methoden hat. Einige möchten vielleicht, dass Sie Ihren eigenen Stack mit einem Array fester Länge implementieren, nur um zu sehen, ob Sie es können. – Dan

4

Das Problem mit dem Java-Ansatz ist, dass seine Methode pop() mindestens zwei Effekte hat: Entfernen eines Elements und Zurückgeben eines Elements. Dies verstößt gegen das Prinzip der einheitlichen Verantwortung des Softwaredesigns, was wiederum die Designkomplexität und andere Probleme öffnet. Es beinhaltet auch eine Leistungseinbuße.

In der STL Art der Dinge ist die Idee, dass manchmal, wenn Sie pop() Sie nicht an dem Artikel interessiert sind. Sie möchten nur den Effekt, dass das oberste Element entfernt wird. Wenn die Funktion das Element zurückgibt und Sie es ignorieren, dann ist das eine verschwendete Kopie.

Wenn Sie zwei Überladungen angeben, eine, die eine Referenz akzeptiert, und eine andere, die dem nicht zulässt, dann können Sie wählen, ob er (oder sie) an dem zurückgegebenen Element interessiert ist oder nicht. Die Leistung des Anrufs ist optimal.

Die STL Überlastung nicht die pop() Funktionen, sondern teilt diese in zwei Funktionen: back() (oder top() im Fall des std::stack Adapter) und pop(). Die back() Funktion gibt nur das Element zurück, während die pop() Funktion es nur entfernt.

+1

'top' nicht' front' –

+1

und 'top' ruft' zurück' auf – Potatoswatter

+4

Eine Funktion, die zwei Effekte als eine einzige atomare Operation hat, verstößt nicht gegen das Prinzip der einfachen Verantwortung des Software-Designs. –

0

Der einzige Grund, warum ich für die Verwendung dieser Syntax in C++ sehen:

void pop(Item& removed); 

ist, wenn Sie unnötige Kopien besorgt sind, statt.

, wenn Sie die Item zurückkehren, es kann benötigen eine zusätzliche Kopie des Objekts, die teuer sein kann.

In der Realität sind C++ - Compiler sehr gut in Kopie Elision, und implementieren fast immer Rückgabewert-Optimierung (oft auch wenn Sie mit deaktivierten Optimierungen kompilieren), was den Punkt macht, und kann sogar bedeuten, die einfache "Rückgabewert "Version wird schneller in einigen Fällen.

Aber wenn Sie vorzeitige Optimierung sind in (wenn Sie besorgt, dass der Compiler könnte die Kopie nicht optimieren weg, obwohl es in der Praxis wird es tun), könnte man für „Rückkehr“ Parameter argumentieren indem Sie einem Referenzparameter zuweisen.

Weitere Informationen here

0

IMO, eine gute Signatur für die eqivalent von pop Funktion Java in C++ wäre so etwas wie:

boost::optional<Item> pop(); 

Typen Option Mit dem besten Weg ist, etwas zurückzugeben, das kann oder möglicherweise nicht verfügbar.

1

Mit C++ 0x macht das Ganze wieder schwer.

Als

stack.pop(item); // move top data to item without copying 

es ermöglicht, effizient das oberste Element aus dem Stapel zu bewegen. Wobei

item = stack.top(); // make a copy of the top element 
stack.pop(); // delete top element 

solche Optimierungen nicht zulässt.

+0

'item = std :: move (stack.top()); stack.pop(); 'funktioniert jedoch effizient. Interessant ist, dass der Hauptgrund für dieses Design in C++ 03 (Ausnahmesicherheit) in C++ 11 nicht mehr der Fall ist (weil Move-Konstruktoren No-Throw sein sollten). – Mankarse

Verwandte Themen