2012-11-22 8 views
7

Ich habe Zweifel über die Rückgabe von Std :: String als Konstante const.Return std :: string als const Referenz

class sample 
{ 
public: 
    std::string mString; 
    void Set(const std::string& s) 
    { 
    mString = s; 
    } 
    std::string Get() 
    { 
    return mString; 
    } 
}; 

In der Set-Funktion bin ich vorbei das std :: string als konstante Referenz, const, weil sein Wert innerhalb der Funktion nicht verändert wird.

Und in Get-Funktion, eigentlich bin ich hier verwirrt. Rückgabewert std :: string als Wert macht mehr Sinn. Aber ich bin mir nicht sicher, ob das Übergeben der Zeichenfolge als const-Referenz irgendwelche Vorteile bringt. Indem ich die Saite als Referenz zurückbeziehe, werde ich die Geschwindigkeit erhöhen, denke ich. Aber ich bin mir nicht sicher. Aber es als "const" zurückzubringen, bringt einen Nutzen für dieses?

Antwort

10

Die Rückgabe per Referenz oder Konst-Referenz hat keine Geschwindigkeitsdifferenz - beide sind sehr schnell, da sie nur einen Verweis auf das Originalobjekt zurückgeben, es ist kein Kopieren beteiligt.

Ein Objekt, das von einer (nicht konstanten) Referenz zurückgegeben wird, kann über diese Referenz geändert werden. In Ihrem konkreten Beispiel ist mString öffentlich, so dass es trotzdem (und direkt) geändert werden kann. Der übliche Ansatz mit Getter und Setter (und der Hauptgrund für ihre Einführung) ist die Kapselung - Sie erlauben nur den Zugriff auf Ihre Datenmitglieder über den Getter/Setter, so dass Sie ungültige Werte erkennen, auf Wertänderungen reagieren und reagieren können Halten Sie im Allgemeinen die Implementierungsdetails Ihrer Klasse verborgen. Getter kommen also normalerweise durch const Referenz oder nach Wert zurück.

Wenn Sie jedoch durch const-Verweis zurückgeben, bindet es Sie an immer halten Sie eine Instanz von std::string in Ihrer Klasse, um die Referenz zu sichern. Das heißt, auch wenn Sie später Ihre Klasse so umgestalten möchten, dass sie die Zeichenfolge im Getter im Handumdrehen berechnet, anstatt sie intern zu speichern, können Sie dies nicht tun. Sie müssten gleichzeitig Ihre öffentliche Schnittstelle ändern, wodurch der Code unter Verwendung der Klasse beschädigt werden kann.Zum Beispiel, wie lange, wie Sie durch const-Referenz zurückkehren, ist dies absolut gültige Code:

const std::string *result = &aSample.Get(); 

Dieser Code wird natürlich einen baumelnden Zeiger nicht mehr kompilieren wenn Get() produzieren wird durch Wert zurückzukehren geändert anstelle von const Referenz. (Danke an Steve Jessop für die Korrektur)

Zusammengefasst, der Ansatz, den ich nehmen würde, ist mString privat zu machen. Get() kann nach Wert oder durch const-Verweis zurückgegeben werden, je nachdem, wie sicher Sie sind, dass Sie immer eine Zeichenfolge gespeichert haben. Die Klasse würde dann wie folgt aussehen:

class sample 
{ 
    std::string mString; 

public: 
    void Set(const std::string &s) 
    { 
    mString = s; 
    } 
    std::string Get() const 
    { 
    return mString; 
    } 
}; 
+1

"Dieser Code erzeugt natürlich einen ungeordneten Zeiger, wenn Get() geändert wird, um durch Wert anstelle von const-Verweis zurückzugeben." - nein es wird nicht, es wird aufhören zu kompilieren ("kann die Adresse eines temporären nicht nehmen"). Was ist besser als Kompilieren, aber mit UB. Eigentlich könnte * es aufgrund einer Erweiterung kompilieren, aber ich denke nicht, dass das eine allgemeine Erweiterung ist und der Standard immer noch eine Diagnose erfordert, so dass der Benutzer zumindest gewarnt werden sollte. –

+0

@SteveJessop Danke, ich habe die Antwort korrigiert. – Angew

0

Geben Sie es als Referenz zurück. Wenn eine Kopie benötigt wird, kann sie mit Sicherheit aus dieser Referenz erstellt werden.

+0

Wie wäre es, es als 'const' zurückzugeben? –

+0

Wenn Sie möchten, dass Ihre Zeichenfolge nur durch die Methode 'Set' modifiziert wird, definieren Sie 'Get' als' const std :: string & Get() const {return mString; } ' – chill

13

Das Problem der Entscheidung, wie ein nicht-triviale aus einer Art eines Container-Objekt zurück ist eigentlich nicht-trivial:

  • Wenn die Klasse, von dem Sie Ihren Wert zurückgeben, jede Art von Einschränkung für die erlegt Object, Sie können nicht eine Referenz zurückgeben, da diese die Möglichkeit verliert, ihre Invarianten zu erzwingen. Natürlich ist die Rückgabe eines Objekts durch nicht-Referenz const nur sinnvoll, wenn Objekt, das die Member-Funktion aufgerufen wird, auch nicht const ist.
  • Wenn Sie einen const Verweis auf ein Objekt ausstellen, würde das Problem mit den Invarianten vermieden, aber impliziert, dass ein Objekt des entsprechenden Typs tatsächlich intern als Implementierungsdetail beibehalten wird.
  • Die Rückgabe eines Objekts nach Wert kann erhebliche Kosten für das Kopieren des Objekts verursachen.

Wenn Sie Klasse ist weiterhin eine tragfähige monitor Sie auf jeden Fall das Objekt nach Wert zurückkehren wollen, weil sonst kann das Objekt mutiert werden, bevor der Anrufer eine Chance hatte, es zu kopieren.

Grundsätzlich ist keine der Möglichkeiten ideal. Im Zweifelsfall gebe ich den Wert zurück, es sei denn, es ist bekannt, dass das Objekt teuer zu kopieren ist. In diesem Fall kann ich durch const& zurückgeben.

+1

Ja. Ich weiß, das ist eine alte Frage, aber gerade bei einer Schnittstelle, bei der man keine Restriktionen bei der Implementierung geben will, ist die Rückgabe meist besser. –

+0

Möchten Sie stattdessen ein unique_ptr zurückgeben? –

0

Das häufigste, was hier zu tun wäre, um den Wert als const Verweis zurück, dann können Sie einen Verweis verwenden oder den Wert bei Bedarf kopieren:

const std::string& Get() const 
{ 
    return mString; 
} 

sample mySample; 
const std::string &refString = mySample.Get(); // Const-reference to your mString 
const std::string copyString = mySample.Get(); // Copy of your mString 

Wenn Sie wirklich benötigen eine Kopie der Zeichenfolge zurück, dann können Sie die Zeichenfolge Rückgabewert Kopieren vermeiden, indem sie unter Verwendung von „The Most Important Const“:

sample mySample; 
const std::string &myString = mySample.Get(); 
// myString is now valid until it falls out of scope, even though it points to a "temporary" variable