2010-08-07 11 views
32

Ich ziehe von Java zu C++ um und bin etwas verwirrt von der Flexibilität der Sprache. Ein Punkt ist, dass es drei Möglichkeiten gibt, Objekte zu speichern: Einen Zeiger, eine Referenz und einen Skalar (das Objekt selbst wird gespeichert, wenn ich es richtig verstehe).Wann soll ein Zeiger, Skalar und Referenz in C++ zurückgegeben werden?

Ich neige dazu, Referenzen wo möglich zu verwenden, weil das so nah wie möglich an Java ist. In einigen Fällen, z.B. Getter für Attribute abgeleitet, ist dies nicht möglich:

MyType &MyClass::getSomeAttribute() { 
    MyType t; 
    return t; 
} 

Diese nicht kompilieren, weil t nur im Rahmen von getSomeAttribute() existiert und wenn ich einen Hinweis darauf zurück, wäre es nirgendwo Punkt, bevor der Client sie verwenden können .

Deshalb bin ich mit zwei Optionen links:

  1. einen Zeiger
  2. Return ein Skalar

einen Zeiger zurückkehrend würde wie folgt aussehen:

MyType *MyClass::getSomeAttribute() { 
    MyType *t = new MyType; 
    return t; 
} 

Diese würde funktionieren, aber der Client müsste diesen Zeiger auf NULL überprüfen, um zu sein wirklich sicher, etwas, das mit Referenzen nicht notwendig ist. Ein anderes Problem ist, dass der Anrufer sicherstellen muss, dass t freigegeben ist, ich würde lieber nicht damit umgehen, wenn ich es vermeiden kann.

Die Alternative wäre, das Objekt selbst (Skalar) zurückzukehren:

MyType MyClass::getSomeAttribute() { 
    MyType t; 
    return t; 
} 

Das ist ziemlich einfach ist und genau das, was ich in diesem Fall will: Es ist wie eine Referenz fühlt und es kann nicht null sein. Wenn das Objekt nicht im Gültigkeitsbereich des Clients enthalten ist, wird es gelöscht. Ziemlich praktisch. Aber ich sehe selten jemanden, der das macht, gibt es einen Grund dafür? Gibt es eine Art Leistungsproblem, wenn ich einen Skalar anstelle eines Zeigers oder einer Referenz zurückgebe?

Was ist der häufigste/eleganteste Ansatz, um dieses Problem zu lösen?

+12

"Ich ziehe von Java nach C++" bedeutet nichts. Java ist nicht C++. Java wird niemals C++ sein. Das Nachdenken über Java während der Programmierung von C++ hilft Ihnen nicht, C++ zu verstehen oder Ihnen zu helfen, gute Entscheidungen zu treffen. Jede Sprache wird Ihnen nicht helfen, gutes C++ außer C++ zu programmieren, also vergessen Sie, andere Sprachen zu kennen, weil sie nicht C++ sind. Sie müssen lernen, C++, wenn Sie in C++ programmieren möchten, nicht versuchen, eine bereits vorhandene Sprache in C++ zu verwandeln, weil das nicht funktioniert, da nur C++ C++ ist. [Könnte ich ein gutes Buch vorschlagen?] (Http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) – GManNickG

+3

Ein Verweis ist nicht nahe, was Java tut. Die nächste Sprache, die nur für Java konstruiert wird, wäre die Rückgabe eines Zeigers. Das berücksichtigt jedoch nicht die Speicherverwaltung. Das nächste C++ - Konstrukt für Java wäre also die Rückgabe eines gemeinsamen Zeigers. std :: tr1 :: shared_ptr (oder std :: shared_ptr oder boost :: shared_ptr (je nachdem, welche Version Sie verwenden)). Sie können neu und nach Herzenslust Inhalt vergessen und in der Regel wird das Objekt Müll korrekt gesammelt. Aber C++ ist kein Java. Sie müssen lernen, wie Sie Werte korrekt verwenden. Bringen Sie Java-Konzepte nicht in C++, die Sprachen sind einfach anders. –

+6

@ Gman - Ok, also C++ ist nicht Java, aber ignorieren Sprachspezifikationen. In Java ausgedrückte Ideen können in C++ übersetzt werden. Ich programmiere oft in mehreren Sprachen, oft unterscheidet sich die Syntax aber nicht die Semantik. – brumScouse

Antwort

24

Rückgabewert. Der Compiler kann die Kopie optimieren, also ist das Endergebnis das, was Sie wollen. Ein Objekt wird erstellt und an den Aufrufer zurückgegeben.

Ich denke der Grund, warum Sie Leute nur selten sehen, ist, weil Sie den falschen C++ Code suchen. ;) Die meisten Leute, die aus Java kommen, fühlen sich unbehaglich, wenn sie so etwas tun, also rufen sie überall new an. Und dann bekommen sie Speicherlecks überall, müssen nach NULL und all den anderen Problemen suchen, die sie verursachen können. :)

Es könnte auch erwähnenswert sein, dass C++ Referenzen mit Java-Referenzen sehr wenig gemeinsam haben. Ein Verweis in Java ist einem Zeiger viel ähnlicher (er kann neu positioniert oder auf NULL gesetzt werden). Tatsächlich sind die einzigen wirklichen Unterschiede, dass ein Zeiger auch auf einen Garbage-Wert zeigen kann (wenn er nicht initialisiert ist oder auf ein Objekt zeigt, das seinen Gültigkeitsbereich verlassen hat) und dass Sie Pointer-Arithmetik auf einem Zeiger ausführen können eine Anordnung. Ein C++ - Verweise ist ein Alias ​​für ein Objekt. Eine Java-Referenz verhält sich nicht so.

+0

Danke, also gibt es keinen wirklichen Vorbehalt, ein abgeleitetes Attribut nach Wert zurückzugeben? Großartig :) Wie wäre es mit der Rückgabe eines Mitglieds nach Wert anstatt nach Referenz, wie ich es gerade mache? Oder Parameter nach Wert übergeben. Ist das ineffizient? – theone

+0

Es gibt Vorbehalte ziemlich überall in C++. Aber solange Sie das Offensichtliche im Hinterkopf behalten, dass Sie * eine Kopie * zurückgeben, anstatt das Objekt selbst, und solange Ihre Kopierkonstruktoren korrekt geschrieben sind, sollte es in Ordnung sein. – jalf

+0

Wie für die Übergabe von Parametern nach Wert, es hängt davon ab. Erneut darf der Compiler die Kopie entfernen (und das geschieht typischerweise so aggressiv). Außerdem gibt es Fälle, in denen Pass-by-Copy * schneller * sein kann (http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/). Aber oft passieren die Leute die const-Referenz, statt dem Compiler zu vertrauen, um die Kopie zu optimieren. – jalf

3

Vermeiden Sie einfach die Verwendung von Zeigern und dynamischer Zuweisung durch new wo immer möglich.Verwenden Sie stattdessen Werte, Referenzen und automatisch zugeordnete Objekte. Natürlich kann man die dynamische Zuordnung nicht immer vermeiden, aber es sollte ein letzter Ausweg sein, nicht ein erster.

0

Rückgabe nach Wert ist eine gängige Praxis in C++. Wenn Sie jedoch ein Objekt übergeben, übergeben Sie es als Referenz.

Beispiel

main() 
    { 

     equity trader; 

     isTraderAllowed(trader); 

     .... 
    } 

    bool isTraderAllowed(const equity& trdobj) 
    { 
      ... // Perform your function routine here. 
    } 

Obiges ist ein einfaches Beispiel für ein Objekt unter Bezugnahme geben. In Wirklichkeit hätten Sie eine Methode mit dem Namen isTraderAllowed für die Klasse Equity, aber ich zeigte Ihnen eine echte Verwendung der Weitergabe als Referenz.

+0

Dies ist nicht immer der Fall - manchmal möchten Sie vielleicht nach Wert übergeben. – Puppy

2

Die Rückgabe nach Wert kann zu Leistungseinbußen führen, da dies bedeutet, dass das Objekt kopiert werden muss. Wenn es sich um ein großes Objekt wie eine Liste handelt, ist diese Operation möglicherweise sehr teuer.

Aber moderne Compiler sind sehr gut darüber, dass dies nicht passieren. Die C++ - Standards geben ausdrücklich an, dass der Compiler unter bestimmten Umständen Kopien kopieren darf. Die bestimmte Instanz, die in dem von Ihnen angegebenen Beispielcode relevant wäre, wird als "Rückgabewertoptimierung" bezeichnet.

Persönlich gebe ich durch (normalerweise const) Verweis zurück, wenn ich eine Mitgliedsvariable zurückgebe und irgendeine Art des intelligenten Zeigergegenstandes irgendeiner Art zurückgebe (häufig ::std::auto_ptr), wenn ich etwas dynamisch zuweisen muss. Ansonsten gebe ich nach Wert zurück.

Ich habe auch sehr häufig const Referenzparameter, und das ist sehr üblich in C++. Dies ist eine Möglichkeit, einen Parameter zu übergeben und zu sagen: "Die Funktion darf das nicht berühren". Grundsätzlich ein schreibgeschützter Parameter. Es sollte nur für Objekte verwendet werden, die komplexer sind als eine einzelne Ganzzahl oder ein Zeiger.

Ich denke, eine große Änderung von Java ist, dass const ist wichtig und sehr häufig verwendet. Lerne, es zu verstehen und es zu deinem Freund zu machen.

Ich denke auch, dass Neils Antwort richtig ist, wenn man sagt, dass es eine gute Idee ist, eine dynamische Zuweisung zu vermeiden, wann immer es möglich ist. Sie sollten Ihr Design nicht zu sehr verzerren, um dies zu ermöglichen, aber Sie sollten definitiv Design-Entscheidungen bevorzugen, in denen es nicht passieren muss.

+0

Großer Rat, danke.Ich liebe wirklich const, ich kann nicht glauben, wie ich ohne es lebte.Ich habe immer noch Probleme zu entscheiden wann zum Beispiel : Ich habe eine Klasse, die gute alte prozedurale OpenGL-Aufrufe kapselt. Wenn eine Methode OpenGL-Sachen macht, ist es const oder nicht? Ich meine, es verändert irgendeinen Zustand irgendwo im Programm. – theone

+0

@theone: Das übliche Problem ist "ändert es den * logischen * Zustand des Objekts, das Mitglied ist". Wenn Sie mir ein const OpenGL-Objekt geben, welche Operationen würde es für mich sinnvoll machen, sie auszuführen? Sei nicht zu nervös darüber, ob es irgendwo einen Zustand verändert. – jalf

+0

@theone, @jalf - Ich würde hinzufügen, dass "den logischen Zustand ändern" sollte eine formale Definition von "Ändert es das semantische Ergebnis (dh es könnte sich ändern, die Zeit, die es dauert, aber nicht, was es tut) von jedem anschließender Aufruf einer Memberfunktion desselben Objekts? ". Natürlich wird "const" unter bestimmten Umständen eine strengere Definition haben, weil der Zustand des Objekts im Speicher sein kann, der eigentlich nicht schreibbar ist. – Omnifarious

0

Ein Punkt in Bezug vorbei Wert oder Referenz:
Optimierungen betrachtet, eine Funktion, unter der Annahme, Inline, wenn der Parameter als „const Datatype ObjektName“ dass Datatype alles selbst Primitiven sein könnte erklärt wird, wird kein Objekt Kopie sein beteiligt; und wenn sein Parameter als "const DataType & objectName" oder "DataType & objectName" deklariert ist, dass DataType wieder alles sein könnte, selbst Primitives, ist keine Adressierung oder Zeiger beteiligt. In beiden vorherigen Fällen werden Eingabeargumente direkt im Assemblercode verwendet.

Ein Punkt in Bezug auf Referenzen:
Ein Verweis nicht immer ein Zeiger ist, wie beispielsweise, wenn Sie in den Körper einer Funktion folgenden Code haben, ist die Referenz kein Zeiger:

int adad=5; 
int & reference=adad; 

Ein Punkt in Bezug auf Rückgabe nach Wert:
wie einige Leute erwähnt haben, mit guten Compilern mit der Fähigkeit der Optimierung, die Rückgabe von Wert eines beliebigen Typs wird keine zusätzliche Kopie verursachen.

Ein Punkt zur Rückgabe von Referenz:
Bei inline Funktionen und Optimierungen, werden durch Bezugnahme zurückkehr nicht Adresse Wegnahme oder Zeiger beinhalten.

+1

All das Fett beeinträchtigt wirklich das, was Sie sagen wollen. –

Verwandte Themen