410 Stimmen

Ist die Praxis, eine C++-Referenzvariable zurückzugeben, schlecht?

Dies ist meiner Meinung nach etwas subjektiv; ich bin mir nicht sicher, ob die Meinung einhellig ist (ich habe viele Codeschnipsel gesehen, in denen Referenzen zurückgegeben werden).

Laut einem Kommentar gegenüber die Frage, die ich gerade gestellt habe, bezüglich der Initialisierung von Referenzen Die Rückgabe eines Verweises kann böse sein, weil es [so wie ich es verstehe] leichter ist, das Löschen zu verpassen, was zu Speicherlecks führen kann.

Das beunruhigt mich, denn ich bin den Beispielen gefolgt (es sei denn, ich bilde mir etwas ein) und habe dies an mehreren Stellen getan... Habe ich das falsch verstanden? Ist es böse? Wenn ja, wie böse?

Ich fühle, dass wegen meiner gemischten Tasche von Zeigern und Referenzen, kombiniert mit der Tatsache, dass ich neu in C++ bin, und insgesamt Verwirrung über was zu verwenden, wenn, meine Anwendungen müssen Speicherleck Hölle sein ...

Ich verstehe auch, dass die Verwendung von intelligenten/gemeinsamen Zeigern allgemein als der beste Weg zur Vermeidung von Speicherlecks akzeptiert wird.

504voto

GManNickG Punkte 476445

Im Allgemeinen ist die Rückgabe eines Verweises völlig normal und kommt immer wieder vor.

Wenn Sie meinen:

int& getInt() {
    int i;
    return i;  // DON'T DO THIS.
}

Das ist ganz und gar böse. Der Stack-allocated i wird verschwinden und Sie beziehen sich auf nichts. Auch das ist böse:

int& getInt() {
    int* i = new int;
    return *i;  // DON'T DO THIS.
}

Denn jetzt muss der Kunde schließlich das Fremde tun:

int& myInt = getInt(); // note the &, we cannot lose this reference!
delete &myInt;         // must delete...totally weird and  evil

int oops = getInt(); 
delete &oops; // undefined behavior, we're wrongly deleting a copy, not the original

Beachten Sie, dass rvalue-Referenzen nach wie vor nur Referenzen sind, so dass alle bösen Anwendungen die gleichen bleiben.

Wenn Sie etwas zuweisen wollen, das außerhalb des Funktionsumfangs liegt, verwenden Sie einen Smart Pointer (oder allgemein einen Container):

std::unique_ptr<int> getInt() {
    return std::make_unique<int>(0);
}

Und jetzt speichert der Client einen Smart Pointer:

std::unique_ptr<int> x = getInt();

Referenzen eignen sich auch für den Zugriff auf Dinge, von denen Sie wissen, dass die Lebensdauer auf einer höheren Ebene offen gehalten wird, z. B.:

struct immutableint {
    immutableint(int i) : i_(i) {}

    const int& get() const { return i_; }
private:
    int i_;
};

Hier wissen wir, dass es okay ist, einen Verweis auf i_ denn was auch immer uns aufruft, verwaltet die Lebensdauer der Klasseninstanz, also i_ wird mindestens so lange leben.

Und natürlich ist nichts falsch daran, einfach zu sein:

int getInt() {
   return 0;
}

Wenn die Lebensdauer dem Anrufer überlassen werden soll und Sie nur den Wert berechnen.

Zusammenfassung: Es ist in Ordnung, eine Referenz zurückzugeben, wenn die Lebensdauer des Objekts nach dem Aufruf nicht endet.

70voto

Charlie Martin Punkte 106684

Nein. Nein, nein, tausendmal nein.

Das Übel ist, dass ein Verweis auf ein dynamisch zugewiesenes Objekt erstellt wird und der ursprüngliche Zeiger verloren geht. Wenn Sie new eines Objekts gehen Sie die Verpflichtung ein, eine garantierte delete .

Aber schauen Sie sich z.B. an, operator<< : das doit eine Referenz zurückgeben, oder

cout << "foo" << "bar" << "bletch" << endl ;

wird nicht funktionieren.

61voto

David Thornley Punkte 55244

Sie sollten einen Verweis auf ein vorhandenes Objekt zurückgeben, das nicht sofort verschwindet und bei dem Sie keinen Eigentumsübergang beabsichtigen.

Geben Sie niemals einen Verweis auf eine lokale Variable oder ähnliches zurück, da diese nicht mehr vorhanden ist und nicht mehr referenziert werden kann.

Sie können einen Verweis auf etwas zurückgeben, das von der Funktion unabhängig ist und von dem Sie nicht erwarten, dass die aufrufende Funktion die Verantwortung für die Löschung übernimmt. Dies ist der Fall bei der typischen operator[] Funktion.

Wenn Sie etwas erstellen, sollten Sie entweder einen Wert oder einen Zeiger (normal oder intelligent) zurückgeben. Einen Wert können Sie frei zurückgeben, da er in eine Variable oder einen Ausdruck der aufrufenden Funktion eingeht. Geben Sie niemals einen Zeiger auf eine lokale Variable zurück, da diese verschwinden wird.

46voto

thorhunter Punkte 473

Ich finde die Antworten nicht zufriedenstellend, also füge ich meinen Senf dazu.

Analysieren wir die folgenden Fälle:

Falsche Verwendung

int& getInt()
{
    int x = 4;
    return x;
}

Dies ist offensichtlich ein Fehler

int& x = getInt(); // will refer to garbage

Verwendung mit statischen Variablen

int& getInt()
{
   static int x = 4;
   return x;
}

Das ist richtig, denn statische Variablen sind während der gesamten Lebensdauer eines Programms vorhanden.

int& x = getInt(); // valid reference, x = 4

Dies ist auch bei der Implementierung des Singleton-Musters üblich

class Singleton
{
    public:
        static Singleton& instance()
        {
            static Singleton instance;
            return instance;
        };

        void printHello()
        {
             printf("Hello");
        };

};

Verwendung:

 Singleton& my_sing = Singleton::instance(); // Valid Singleton instance
 my_sing.printHello();  // "Hello"

Betreiber

Standardbibliothekscontainer hängen stark von der Verwendung von Operatoren ab, die eine Referenz zurückgeben, zum Beispiel

T & operator*();

kann in folgenden Bereichen verwendet werden

std::vector<int> x = {1, 2, 3}; // create vector with 3 elements
std::vector<int>::iterator iter = x.begin(); // iterator points to first element (1)
*iter = 2; // modify first element, x = {2, 2, 3} now

Schneller Zugriff auf interne Daten

Es gibt Fälle, in denen & für den schnellen Zugriff auf interne Daten verwendet werden kann

Class Container
{
    private:
        std::vector<int> m_data;

    public:
        std::vector<int>& data()
        {
             return m_data;
        }
}

mit Nutzung:

Container cont;
cont.data().push_back(1); // appends element to std::vector<int>
cont.data()[0] // 1

JEDOCH kann dies zu Fallstricken wie den folgenden führen:

Container* cont = new Container;
std::vector<int>& cont_data = cont->data();
cont_data.push_back(1);
delete cont; // This is bad, because we still have a dangling reference to its internal data!
cont_data[0]; // dangling reference!

16voto

mmx Punkte 400975

Es ist nicht böse. Wie vieles in C++ ist es gut, wenn es richtig verwendet wird, aber es gibt viele Fallstricke, die Sie beachten sollten, wenn Sie es verwenden (wie die Rückgabe eines Verweises auf eine lokale Variable).

Es gibt gute Dinge, die damit erreicht werden können (wie map[name] = "hello world")

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X