return reference



  • naja ich habe mir gedacht dass ne ref sinn machen könnte, weil das array vermutlich sehr groß wird.



  • drakon schrieb:

    Wegen Zeitoptimierung ist es sicher zu früh, um zu schauen. Ich würde empfehlen das zu nehmen, was eher Sinn macht. (Wahrscheinlich ist das die Rückgabe einer Kopie).

    Das würde ich jetzt aber bestimmt nicht unter Mikrooptimierung zählen. Wenn man wirklich den gesamten Container zurückgeben will (ist eine Designfrage, die kürzlich hier besprochen wurde), ist ein reiner Lesezugriff wahrscheinlicher. Eine Kopie kann zeitaufwändig werden. Wenn man trotzdem eine Kopie braucht, kann man es ja immer noch wie von vlad_tepesch beschrieben tun.

    Es sei denn, du würdest wirklich aus irgendeinem Grund in jedem Fall eine Kopie erfordern, das habe ich aber aus deiner Fragestellung nicht ableiten können.



  • Hallo foogard,

    die Rückgabe einer Referenz ist schneller als eine Rückgabe über eine Kopie. Bei der Verwendung der Container der STL wird bei einer Kopie zunächst ein Verweis auf das Original-Objekt erzeugt und erst bei einer Änderung im Container werden die Daten des Containers kopiert. Das bedeutet, daß der Geschwindigkeitsvorteil bei der Rückgabe über eine Kopie gegenüber einer Rückgabe als Referenz entscheidend vom Design der zurückgegebenen Klasse abhängt.

    Es gibt bei der Rückgabe eines Objektes als Referenz jedoch eines zu beachten:
    Das zurückgegebene Objekt muß nach Beendigung der entsprechenden Funktion noch existieren! Sonst kommt es zu einem Laufzeitfehler.
    Bei folgendem Code z.B:

    int& foo(){
       int x=7;
    //      mache was mit x
       return x;
    };
    

    Hier legst Du die Variable x auf dem Stack an. Am Ende der Funktion gibst du eine Referenz auf diese Variable zurück. Bevor diese jedoch beim Aufrufer ankommt, wird sie mit dem Beenden der Funktion zerstört und ist für die aufrufende Funktion nicht mehr zugänglich.

    edit: @Nexus hat recht, was das Kopieren von STL-Containern angeht. sorry

    edit2: Die Klasse QVector aus der QT-Bibliothek zeigt das von mir oben beschriebene Verhalten.



  • mario_69 schrieb:

    Bei der Verwendung der Container der STL wird bei einer Kopie zunächst ein Verweis auf das Original-Objekt erzeugt und erst bei einer Änderung im Container werden die Daten des Containers kopiert. Das bedeutet, daß der Geschwindigkeitsvorteil bei der Rückgabe über eine Kopie gegenüber einer Rückgabe als Referenz entscheidend vom Design der zurückgegebenen Klasse abhängt.

    Nein, das stimmt so nicht. Wenn der Kopierkonstruktor der STL-Container eingesetzt wird, kommt es zur Konstruktion eines neuen Objekts. Das ist übrigens bei jeder Klasse so; Design spielt hier keine Rolle.

    Compileroptimierungen sind wieder ein anderes Thema.



  • hmm, das problem, dass sich mein objekt ins datennirvana verabschiedet, habe ich dank einer membervariable nicht. aber mir kamm schon der gedanke, wie man so einen von maria_69 geschildeten fall am besten umgeht. evtl static verwenden?





  • foogard schrieb:

    aber mir kamm schon der gedanke, wie man so einen von maria_69 geschildeten fall am besten umgeht. evtl static verwenden?

    Kommt halt auf den jeweiligen Fall an. static kann verwendet werden, wenn ein Objekt bis zum Programmende bestehen soll. Im Normalfall gibt man aber eine Kopie des Objekts zurück.



  • Eine Variable als 'static' zu deklarieren ist nur sinnvoll, wenn Du den Inhalt dieser Variablen beim erneuten Aufruf der Funktion wieder brauchst. Das ist meist ein Ausnahmefall, z.B. wenn Du zählen möchtest, wie oft eine Funktion während des Programmablaufes ausgeführt wird. Ich glaube nicht, das dieses Verhalten von Dir an dieser Stelle gebraucht wird.

    Ein Objekt als Referenz zurückzugeben funktioniert, wenn genau dieses Objekt als Referenz an die Funktion übergeben wurde oder wenn ein Objekt dynamisch angelegt und nicht wieder zerstört wurde. In allen anderen Fällen ist die Rückgabe als Kopie der richtige Weg.



  • static solltest du dafür auf keinen Fall benutzen! Es ist hier z.B. für den Nutzer des Interfaces nicht ersichtlich, dass sowas hier nur crap ergibt:

    const std::string& clone( const char* blubb )
    {
        static std::string s;
        s = blubb;
        return s;
    }
    
    std::string potentially_bug = clone("hello") + clone("world"); // Sollte "hellohello" oder "worldworld" ergeben.
    


  • mario_69 schrieb:

    Ein Objekt als Referenz zurückzugeben funktioniert, wenn genau dieses Objekt als Referenz an die Funktion übergeben wurde oder wenn ein Objekt dynamisch angelegt und nicht wieder zerstört wurde. In allen anderen Fällen ist die Rückgabe als Kopie der richtige Weg.

    Den Fall mit dem dynamisch angelegten Objekt würde ich auch rausnehmen. Speicherlecks können auch den Tod eines Programms verursachen. Dauert eben nur länger.

    Grundsätzlich würde ich es wie Nexus machen. Rückgabe per Wert und dem Compiler vertrauen, dass er die Funktion automatisch umwandelt.



  • @ foogard:
    Das ist ja noch schlimmer, als eine stack-Variable zurück zu geben.
    Weil:
    das Kompiliert der Kompiler höchstwahrscheinlich ohne fehlermeldung.
    man stelle sich aber folgenden Anwendungsfall vor:

    TolleKlasse& superdummeFunktion(Parameter poar1, Parameter par2)
    {
      static TolleKlasse dummIdee;
      ...
      return dummIdee;
    }
    
    void unschuldierBenutzer()
    {
      TolleKlasse& a = superdummeFunktion('s', 324622)
      ...schwierige Berechnungen ..
    
      TolleKlasse& b = superdummeFunktion( ergbniss_der_schwierigen_Berechnung_1, ergbniss_der_schwierigen_Berechnung_2);
    
      vegleiche(a, b);
    }
    

    Na? Ahnst dus?
    Ganz zu schweigen davon, was passiert, wenn man Threads benutzt -.-

    Edit:
    Darüber sollte man sich auch nur untergeordnet Gedanken machen.
    Die modernen Kompiler können 'return value optimaization'.
    Das heißt intern übergeben sie den speicher, der eigendlich der Rückgabewert ist, an die Funktion, so dass die Erzeugung und die Kopie des Rückgabeobjektes vermieden wird.

    Edit:
    In dem Buch von Scott Meyers (More?) Effective C++
    Da gibts auch ein Kapitel über diese Problematik und die hier diskutierten Lösungsvorschläge.



  • Nexus schrieb:

    drakon schrieb:

    Wegen Zeitoptimierung ist es sicher zu früh, um zu schauen. Ich würde empfehlen das zu nehmen, was eher Sinn macht. (Wahrscheinlich ist das die Rückgabe einer Kopie).

    Das würde ich jetzt aber bestimmt nicht unter Mikrooptimierung zählen. Wenn man wirklich den gesamten Container zurückgeben will (ist eine Designfrage, die kürzlich hier besprochen wurde), ist ein reiner Lesezugriff wahrscheinlicher. Eine Kopie kann zeitaufwändig werden. Wenn man trotzdem eine Kopie braucht, kann man es ja immer noch wie von vlad_tepesch beschrieben tun.

    Er macht sich hier zuerst Gedanken, was schneller ist, als zuerst zu schauen, für was er das braucht und das ist unsinnig. Die Rückgabe einer nicht-const-Referez auf eine private Member macht für mich einfach überhaupt keinen Sinn. Da kann ich den auch gleich public machen. Die Rückgabe einer const-Referenz auf eine private Member kann hingegen Sinn machen, da man durchaus lesend darauf zugreifen soll, jedoch schreibend durch eine Funktion.
    Ob man es jetzt dem Programmier überlässt, ob eine Kopier erstellt werden soll, oder nicht (mittels const-Referenz) ist abhängig vom Kontext, wie die Funktion gebraucht wird. Wenn direkt weitergearbeitet werden soll, macht eine Kopie sicher Sinn (Operatorenüberladung).



  • die rückgabe einer nicht konstreferenze macht dann, sinn, wenn du das interface, von der implementierung unabhängig machen willst, aber nicht tausend getter und setter schreiben willst, weil das unhantlich ist.

    Hatte das Beispiel schon mal ähnlich gepostet:

    tepmplate<class t>
    class Vector3:
    {
      public:
    
      T x()const
      { return m_vec[0];}
      T y()const
      { return m_vec[1];}
      T z()const
      { return m_vec[2];}
    
      T& x()
      { return m_vec[0];}
      T& y()
      { return m_vec[1];}
      T& z()
      { return m_vec[2];}
    
      private:
        T m_vals[3]
    }
    

    Bei so elementaren Klassen ist das handling mit setter und getter meist recht unschön



  • vlad_tepesch, bei dir kann man aber geradesogut die Member öffentlich machen. Sind die Aufrufe der Methoden überhaupt immer eindeutig?



  • Nexus schrieb:

    vlad_tepesch, bei dir kann man aber geradesogut die Member öffentlich machen. Sind die Aufrufe der Methoden überhaupt immer eindeutig?

    Die aufrudfe sind eindeutig.

    wenn ich mein member öffentlich mache, habe ich keine x,y,z komponenten, sondern nur noch ein array, was ich nicht möchte.
    Außerdem vielleicht ändert sich die interne struktur irgendwann mal, dann muss man den ganzen bisher geschriebenen Code umbuaen.
    So muss man nur die zugriffsfunktionen neu bauen.

    Di obige Variante kommt den c# propertys am nächsten. der einzige unterscihed sind die ellipsen



  • Okay, das mit der internen Struktur hat was.

    Es kommt auch drauf an, wie man den Vektor verwendet. Wenn man seine Koordinaten bereits zu Beginn setzt und ihn nachher hauptsächlich mit den Operatoren manipuliert, sehe ich auch keinen grossen Sinn darin, einzelne Komponenten nachträglich zu verändern. Funktionen à la SetX() und GetY() finde ich also je nach Anwendung gar nicht so schlecht...


Anmelden zum Antworten