Wann nehme ich Objekt, Zeiger oder Referez?



  • Hallo,
    ich weiss es gibt einige Tutorials wo das im Text erklaert ist, v.a. wie es gemacht wird, aber trotzdem suche ich mal ne verstaendliche Erklaerung fuer folgende grundsaetzliche Probleme. V.a. um fuer mich mal das "warum" (oder besser "wann nehme ich was her") zu beantworten:

    Problem 1: Wenn ich eine Funktion mit einem Parameter schreibe, habe ich doch i.d.R. folgende Moelgichkeiten:
    void func1( Typ param);
    void func2( Typ *param);
    void func3( Typ &param);

    Wann ist welche Definition/Deklaration sinnvoller, warum arbeitet man hauptsaechlich mit Zeigern auf Objekte statt mit den Objekten selber (gut ich denke das liegt wohl an so pass-by-value/pass-by-reference aber was bewirkt der unterschied zwischen *param und &param ausser eines anderen Aufrufs, das is doch beides by-reference???

    Problem 2:
    Wann nehme ich bei einer Funktion folgende Returnwerte sinnvoll:
    Typ func();
    Typ *func();
    Typ &func();

    Irgendwo aehnlich zum obigem, aber auch irgendwo gleiche Frage?!

    Problem 3:
    Wann sollte ich fuer eine Variable eher ein direktes Objekt, wann einen Zeiger und wann eine Referenz erzeugen?

    Ich waere ueber Situationsbeispiele dankbar!!! 🙂



  • naja ne referenz gibt man bvei sachen zurück die etwas mit bestehenden sachen machen sollen z.B. etwas suchen (innem array oder so),

    einen pointer wenn man etwas dynmaisch angelegt hat,

    und einen wet wenn das ergebnis was neues ist was aus den paramentern berechnet wurde oder eben irgend eine info zum ablau der funktion



  • und einen pointer als argument zu übergeben is nur sinnvoll wenn der speicher irgendwie dynamisch freigemacht wurde und nun verändert werden soll bzw. wenn dieser pointer irgendwo einsortiert werden soll



  • nö.
    ganz so einfach ist es nicht.



  • -.- schrieb:

    naja ne referenz gibt man bvei sachen zurück die etwas mit bestehenden sachen machen sollen z.B. etwas suchen (innem array oder so),

    aber nur, wenn Typ nicht const ist. dann verhindert die referenz nur das kopieren!



  • Problem 1:
    Du hast noch wesentlich mehr Möglichkeiten. Du kannst zum Beispiel die Funktion, den Rückgabewert die Parameter und jede mögliche Kombination davon "const" machen.

    Aber um auf die Frage zu kommen:

    call-by-value: Es wird mit einer temporären Kopie der Variablen gearbeitet. Im Falle von Klassen wird für diese der Copykonstruktor aufgerufen und alle Elemente initialisiert ggf. müssen dann nätürlich auch für die Elemente der Klasse die Konstruktoren aufgerufen werden usw. Nicht zu vergessen, beim Beenden der Funktion werden entsprechende Destruktoren aufgerufen. Wie Du siehst, ist das für größere Objekte eine Menge Aufwand um ein simples call-by-value auszuführen. Für eingebaute Typen ist ein call-by-value dagegen sinnvoll (z.B. int, double). Ausserdem gehen alle Änderungen an der temporären Variable am Ende des Programms verloren und werden nicht in der Variable gespeichert, die den Aufruf angestoßen hat.

    call-by-reference (Referenz): Man möchte diesen Aufwand natürlich nicht immer betreiben, deswegen bietet sich die Möglichkeit an, ein call-by-reference durchzuführen. Man muss hierbei beachten, dass eine Referenz immer (!) ein anderer Name für ein bestehendes Objekt ist. Für nicht als "const" deklarierte Referenzen werden ausserdem alle Änderungen am Objekt in der aufrufenden Variablen gespeichert.

    call-by-reference (Adresse): Prinzipiell identisch mit Referenzcall, wobei man nicht mehr von einem anderen Namen für ein Objekt, sondern der Adresse redet.
    (Prinzipiell ist eine Referenz ein konstanter Zeiger auf ein Objekt, der automatisch dereferenziert wird.)

    Wenn Du also eine Begründung suchst, wie was deklariert werden soll, dann musst Du im Anwendungskontext entscheiden, was sinnvoll und korrekt ist.

    Bei Problem 2 und Problem 3 verhält es sich genauso, führe Dir vor Augen was ein Wert/Referenz/Zeiger ist und entscheide, was sinnvoll ist.

    Gruss

    turing



  • Call-by-Value --> Nie wenn du mit Polymorphie arbeitest, sonst hast du kein Derive mehr sondern wirklich ein Base. (da ja ein neues lokales Objekt erstellt wird)



  • Hallo,

    Das Thema ist sicherlich von allgemeinem Interesse, daher will ich versuchen, es möglich vollständig abzudecken. Turing hat bereits einiges richtig erklärt, hier versuche ich mal das ganze abzurunden.

    Zunächst unterscheide ich zwischen dem, was man einer Funktion mitgibt - dem Input - und dem, was man bekommt - dem Output.

    Beim Input ist die Sache ziemlich eindeutig. Alle LW-Objekte (LW: Lightweight bzw. Fliegengewicht) werden per Value und alle anderen mit const Referenz übergeben. Zu den LW-Objekten gehören alle eingebauten Typen von bool bis double und z.B. so Sachen wie Iteratoren. Im Zweifel kann man immer const Referenz nehmen. Also

    void func1( int i );
    void func2( const CBig& aBig );
    

    Der Grund dafür liegt darin, dass möglichst wenig auf den Stack kopiert werden soll; deshalb die Referenz. Die const Referenz deshalb, damit für den Aufrufer schon mit der Funktions-Defintion klar ist, dass sein Input nicht verändert wird und für den Bearbeiter ist es von Vorteil, da er nicht aus Versehen den Input verändern kann.
    Die LW-Objekte werden per Value übergeben, da es beim Kopieren keinen Unterschied macht, ob man ein LW-Objekt kopiert oder eine Referenz darauf, und der Zugriff innerhalb der Funktion ist zumindest theoretisch geringfügig schneller.
    Eine Sonderstellung nehmen noch polymorphe Objekte ein. FireFlow hat's schon erwähnt. Diese dürfen nicht (!) kopiert werden; also bleibt nur Übergabe per const Referenz.
    Warum kein Pointer? Weil ein Pointer auch NULL sein kann. Der Funktions-Schreiber müßte das prüfen und für den NULL-Fall Code schreiben; das ist überflüssig.

    Beim Output wird's schon schwieriger. Ich persönlich strebe an, jeden Output über den Return-Wert zurückzugeben. Jede C++-Funktion sollte meines Erachtens so aussehen wie eine mathematische Funktion.
    ergebnis=func(x,y,z)ergebnis = func( x, y, z )
    Dieses Ergebnis ist immer ein Value, und kein Pointer oder eine Referenz. Denn bei einem Pointer oder einer Referenz muss sich immer jemand um das Löschen des Objekts kümmern, und es ist nie so ganz klar, wer das tun soll. Referenzen von Objekten die innerhalb der Funktion auf dem Stack erzeugt worden sind (also normale lokale Variablen) dürfen nicht per Referenz zurückgegeben werden, da sie dann gar nicht mehr exisitieren.

    Jetzt treten (im wahren Leben 😉 ) sofort vier Probleme auf.
    1. was ist, wenn CBig (zwar kopierbar, aber Kopie wäre aufwendig) das Ergebnis sein soll?
    2. was ist mit polymorphen Objekten, bei denen der Aufrufer der Funktion nur mit der Basisklasse was anfangen kann?
    3. was tun, wenn das Ergebnis aus zwei oder mehr Variablen besteht?
    4. Das Ergebnis-Objekt liegt schon vor, es soll in der Funktion manipuliert werden?

    Zu 1.) CBig
    Ist CBig eine selbstgebaute Klasse, kann man sich überlegen, ob das Kopieren wirklich so aufwendig sein muss. Weil dieser Umstand erschwert es ja auch, CBig in einen STL-Container unterzubringen. Hier hilft das sogenannte 'pImpl-Idiom'. Soll heißen, man trennt CBig in CBig und CBig_Implementierung, schafft alle Member-Variblen nach CBig_Implementierung und hält sich in CBig nur noch einen (Smart-)Pointer auf CBig_Implementierung. Eben den oben erwähnte 'pImpl'. Für den Typ ist boost_shared_ptr< CBig_Implementierung > die erste Wahl. Damit wird CBig 'move-fähig'.
    Eine weitere Möglichkeit CBig 'move-fähig' zu machen ist Move-Joint-Objects. Wer sich dafür interessiert, suche bitte nach 'mojo' und 'Andrei Alexandrescu' - das Thema ist zu fett, un es hier auch noch zu behandeln, und das Verfahren kompiliert auch nicht überall.

    Ist CBig ein (STL-)Container mit (sehr) vielen Elementen, so neige ich das dazu, ihn selbst nicht als Returnwert zurückzugeben, sondern der Funktion einen Iterator mitzugeben, über den sie ihn füllen kann. Beispiel:

    // statt
        std::vector< int > erg = fuelleDenVector(); // ganz viele Elemente
        // alternativ:
        std::vector< int > erg;
        fuelleDenVector( std::back_inserter( erg ) );
    

    .. und schon sind wir bei der ersten Ausnahme von der Regel, dass alle C++-Funktionen wie mathematische Funktionen aussehen sollen.

    Wenn alle Stricke reißen, kann man statt CBig auch einen Smart-Pointer auf CBig zurückgeben. Beispiel:

    std::auto_ptr< CBig > big = gibMirEinBig();
    

    Neben std::auto_ptr<> bietet sich auch noch boost::shared_ptr<> an. Und wer ohne Smart-Pointer Objekte auf dem Heap anlegt, programmiert eh' verkehrt.

    Zu 2.) polymorphe Objekte.
    Wie gerade beschrieben - hier muss man einen Pointer zurückgeben, und damit ist der eben beschriebene Smart-Pointer das Mittel der Wahl.

    Zu 3.) mehrere Variablen
    Ein Fall der öfter vorkommt ist der, dass man neben dem eigentlichen Return-Wert noch ein Flag mitgibt, das die Gültigkeit des Ergebnisses angibt. Prominentes Beispiel ist die insert-Methode einer std::map

    std::pair<iterator, bool> insert(const value_type& x);
    

    Das heißt die beiden Variblen des Ergebnisses werden mit std::pair zu einer zusammengefaßt. Wenn beides LW-Objekte sind, muss man sich auch noch keine Gedanken über zu viel Kopiererei machen.
    Sind es mehr als zwei Variablen, liegt das Problem woanders. Man sollte sich z.B. überlegen ob diese Variablen nicht zusammen ein Objekt bilden, oder ob die Funktion, die man da gerade schreibt nicht besser eine Methode einer Klasse wäre, und die Ergebnisvariablen deren Member.

    Zu 4.) Ergebnis-Variable ist schon da. Also

    CMyClass a;
        macheWasMitA( a ); // alternative: macheWasMitA( &a );
    

    CMyClass müßte so per (nicht const) Referenz oder als Pointer übergeben werden, um der Funktion 'macheWasMitA' den Zugriff auf CMyClass zu erlauben. Das sieht jetzt so ähnlich aus, wie die Situation mit den Füllen von std::vector< int > (s.o.). Mit einem kleinen, aber - wie ich meine - entscheidenden Unterschied. Mein Vorschlag weiter oben war

    std::vector< int > erg;
        fuelleDenVector( std::back_inserter( erg ) );
        // und nicht!
        fuelleDenVector( erg ); // notwendig: Übergabe per Referenz
    

    Der Unterschied besteht darin, dass ich die Möglichkeiten der Funktion stark einschränke. Übergebe ich den ganzen vector, kann die Funktion alles mögliche damit machen, übergebe ich nur den back_inserter, so kann sie nur Elemente hinten anhängen; und - und das ist absolut hilfreich - der Aufrufer sieht das auch im Code ohne in die Funktion hineinzuschauen.

    Nun zurück zu CMyClass und macheWasMitA. Ich meine, diese Konstruktion sollte vermieden werden, da der Aufrufer nicht mehr ohne weiteres übersehen kann, was da wirklich passiert. Wird intern nur eine Methode von CMyClass aufgerufen, so kann man sich die Funktion sparen und die Methode besser gleich so aufrufen. Werden intern mehrere Methoden aufgerufen und noch andere Dinge gemacht und das ganze wird auch noch an mehreren Stellen benötigt, d.h das Auflösen der Funktion würde zu doppeltem Code führen, so fehlt sicher eine Methode von CMyClass, die genau das tut.

    CMyClass::MacheGenauDas(); // nicht const, da das Objekt verändert wird
    

    Damit wandert die Funktionalität in das Innere der Klasse.

    Schwieriger wird es, wenn zwar ein Objekt von CMyClass verändert wird, und daneben noch weitere (Input-)Parameter aufgerufen. In der Funktion wird vielleicht nur mittendrin eine (nicht const) Methode von CMyClass aufgerufen. Kann sein, dass das auch keine Methode von CMyClass sein kann, weil es einfach nicht passt. Dann riecht das ganze schon nach Algorithmus und dort behilft man sich mit Funktoren.
    Angenommen in einer umfangreichen langFunc-Funktion soll mittendrin die Methode (nicht const) CMyClass::Meth1 gerufen werden.
    Angenommen 'langFunc' ist so etwas wie ein Algorithmus. Die moderne Variante könnte dann so aussehen: Aus 'langFunc' wird zunächst ein Template gemacht

    template< typename F >
    void langFunc( const F& fktr, .. weiter Paramter )
    {
        // .. viel Code
        fktr( .. );  // <- hier wird was verändert
        // .. noch mal Code
    }
    
    // der Aufruf
        CMyClass a;
        langFunc( boost::bind( &CMyClass::Meth1, &a, usw. ), .. weitere Parameter );
    

    Statt boost::bind kann man manchmal auch std::bind* oder auch immer selbstgeschriebene Funktoren verwenden.
    Der Vorteil liegt wieder darin, dass der Aufrufer - ohne jeden Kommentar - sofort sieht, dass 'langFunc' nur 'Meth1' aufruft, bzw. aufrufen kann. D.h. dadurch wird die Schnittstelle zwischen dem Aufrufer und 'langFunc' genau daruf reduziert.

    Grundsätzlich gilt die Regel: mache Schnittstellen zu groß wie nötig und so klein wie nötig.

    .. und Funktionsaufrufe sind Schnittstellen. Zum Thema Schnittstellen kann man noch sehr viel mehr sagen, aber das soll erstmal reichen.

    Alle in diesem Posting gemachten Aussagen sind nicht als absolut zu betrachten, es gibt natürlich immer Ausnahmen. Aber wenn die Ausnahme zur Regel wird, läuft irgendwo was verkehrt.

    Gruß
    Werner



  • Danke, v.a. der Beitrag von Turing und Werner Salomon hat mir ziemlich die Augen geoffnet.
    C++ ist ja schon nicht ganz so einfach, aber v.a. die Fragen nach dem "was nehme ich denn nun wann her und warum" stellen noch mal ganz eigene Probleme dar.

    Warum kein Pointer? Weil ein Pointer auch NULL sein kann. Der Funktions-Schreiber müßte das prüfen und für den NULL-Fall Code schreiben; das ist überflüssig.

    Das leuchtet ein aber heisst das, dass ich also von den zwei alternativen Wegen einen pass-by-reference zu realisieren, also dann nur immer den per Referenz nehmen sollte? Gibt es keine Vorteile/Situationen der Uebergabe der "Adresse"?
    Das war mir bei Turing etwas zu theoretisch, sry 🙂
    Gut - eine Referenz ist immer ein anderer Name fuer ein Objekt, das hab ich auch gelesen, in wie fern wirkt sich das hier aus? Wenn man nun eine "Adresse" uebergibt ist doch der Endeffekt der selbe: pass-by-reference. Hab ich das richtig verstanden, dass eben die Referenz, also der andere Name, quasi die Adresse des Objektes ist (diese muss vorhanden sein, wenn das obj existiert) und die direkte Uebergabe der Adresse auch null sein kann, weil das Obj evtl noch nicht existiert, kann man das nicht auch irgendwie ausnutzen?



  • Werner Salomon schrieb:

    Ist CBig ein (STL-)Container mit (sehr) vielen Elementen, so neige ich das dazu, ihn selbst nicht als Returnwert zurückzugeben, sondern der Funktion einen Iterator mitzugeben, über den sie ihn füllen kann.

    das kannste inzwischen einstellen. die compiler könne RVO echt gut.
    manche sind noch ein wenig doof, die kannste zwingen mit

    vector<int> getPrimeFactors(int n){
       vector<int> result;//muss erste zeile sein
       bla und result.push_back();
       return result;//muss einziger returnpoint sein
    }
    

    bzw

    Foo bar(){
       bla und blubb;
       return Foo(wasBerechnetes);//muss einziger returnpoint sein
    }
    

    ich habe den eindruck, der GCC schert sich gar nicht mehr um sowas, sondern macht immer, was ich meine. und daß MS das auch schafft, vermute ich einfach mal.



  • volkard schrieb:

    ... die compiler könne RVO echt gut.
    ...
    ich habe den eindruck, der GCC schert sich gar nicht mehr um sowas, sondern macht immer, was ich meine. und daß MS das auch schafft, vermute ich einfach mal.

    Hallo Volkard,

    Du hast recht, und in Sachen MS ist Deine Vermutung auch richtig. Selbst der MS-VC6-Compiler beherrscht RVO (return-value-optimization) sehr gut. Auch wenn der Return-Value nicht die erste lokale Variable ist und die Funktion mehr als ein Return-Statement hat.

    Für Nachahmer sei noch darauf hingewiesen, dass RVO nur funktioniert, wenn die Variable außerhab der Funktion unmittelbar initialisiert wird. Beispiel:

    class CMyClass;
        std::vector< CMyClass > v = macheVieleMyClass(); // <- hier verhindert RVO eine überflüssige Kopie
    
        // ungünstig ist:
        std::vector< CMyClass > v;
        v = macheVieleMyClass();  // hier wird der Ergebnis-Vektor zwangsläufig kopiert.
    

    Gruß
    Werner



  • Corcovado schrieb:

    Warum kein Pointer? Weil ein Pointer auch NULL sein kann. Der Funktions-Schreiber müßte das prüfen und für den NULL-Fall Code schreiben; das ist überflüssig.

    Das leuchtet ein aber heisst das, dass ich also von den zwei alternativen Wegen einen pass-by-reference zu realisieren, also dann nur immer den per Referenz nehmen sollte? Gibt es keine Vorteile/Situationen der Uebergabe der "Adresse"?
    Das war mir bei Turing etwas zu theoretisch, sry 🙂
    Gut - eine Referenz ist immer ein anderer Name fuer ein Objekt, das hab ich auch gelesen, in wie fern wirkt sich das hier aus? Wenn man nun eine "Adresse" uebergibt ist doch der Endeffekt der selbe: pass-by-reference. Hab ich das richtig verstanden, dass eben die Referenz, also der andere Name, quasi die Adresse des Objektes ist (diese muss vorhanden sein, wenn das obj existiert) und die direkte Uebergabe der Adresse auch null sein kann, weil das Obj evtl noch nicht existiert, kann man das nicht auch irgendwie ausnutzen?

    Natürlich gibt es Situationen, bei denen man einen Pointer übergibt. Ich hatte ja schon drauf hingewiesen, das alle von mir gemachten Aussagen nicht absolut sind.
    Eine Standardsituation ist genau die, wenn der Fall "gar kein Objekt vorhanden" mit zur Schnittstelle gehört. Zum Beispiel bei Verwendung optionaler Parameter.

    void func( .., const CMyClass pMyClass = 0 );
    

    Die Alternative mit Referenz - zum Beispiel so

    void func( .., const CMyClass& aMyClass = CMyClass() );
    

    ist u.U. gar nicht praktikabel. Hier ist der Pointer sicher die bessere Wahl. Der Programmierer von 'func' muss dann natürlich den 0-Fall berücksichtigen.

    Daneben gibt's im wahren Leben noch die C-APIs, historisch gewachsenen Code und jede Menge Zwänge wie z.B. die Rückgabe-Mechanismen bei COM-Schnittstellen von MS. Aber da hat man eh' keine Wahl. Meine Ausführungen bezogen sich eher auf den Fall, wo Du vor der Wahl stehst und Dich fragst "Wie sollte ich es machen?"

    Gruß
    Werner



  • Nochmal zur Unterscheidung zwischen Referenzen und Pointern:

    In den meisten Fällen würde ich Objektübergabe per Referenz bevorzugen. Der Code wird meiner Meinung nach besser lesbar.
    Ich rufe eine Funktion auf, diese soll etwas mit meinem existierenden Objekt machen, also übergebe ich sozusagen das Objekt.

    Objektübergabe per Pointer, irgendwie finde ich das nicht schick. Aus "." wird "->" das hat irgendwas von einem Graphen...

    Da bevorzugt jeder sicher seinen eigenen Stil.

    Bleiben wir doch dabei und programmieren lieber mit Objekten und erzeugen Code, der mit Objekten arbeitet. Überlassen wir es dem Compiler alles in Adressen umzusetzen und drücken dies auch durch unseren Codierstil aus.

    Auf der anderen Seite: Bei rekursiven Datenstrukturen (Bäume, Listen, ...) empfehlen sich Pointer.

    Gruss

    turing



  • Werner Salomon schrieb:

    template< typename F >
    void langFunc( const F& fktr, .. weiter Paramter )
    {
        // .. viel Code
        fktr( .. );  // <- hier wird was verändert
        // .. noch mal Code
    }
    
    // der Aufruf
        CMyClass a;
        langFunc( boost::bind( &CMyClass::Meth1, &a, usw. ), .. weitere Parameter );
    

    warum soll man hier boost::bind verwenden?



  • ich kenne da einen, der hat ne ganz komische meinung dazu.

    hier rät er ganz allgemein von referenzen als andere namen ab.
    http://www.volkard.de/vcppkold/referenzen.html

    hier rät er davon ab, referenzen als funktionsparameter zu nehmen.
    http://www.volkard.de/vcppkold/call_by_reference.html

    und hier sagt er, daß man aber const-referenzen als parameter ruhig und viel benutzen soll.
    http://www.volkard.de/vcppkold/constreferenzen.html

    ist irgendwie schlimm, wie sich der über eure einfache argumentation wie "-> ist nicht schön" oder "weil referenzen nicht 0 haben können, sind sie besser" hinwegsetzt. und das schon seit 5 jahren.



  • volkard schrieb:

    ich kenne da einen, der hat ne ganz komische meinung dazu.

    hier rät er ganz allgemein von referenzen als andere namen ab.
    http://www.volkard.de/vcppkold/referenzen.html

    hier rät er davon ab, referenzen als funktionsparameter zu nehmen.
    http://www.volkard.de/vcppkold/call_by_reference.html

    und hier sagt er, daß man aber const-referenzen als parameter ruhig und viel benutzen soll.
    http://www.volkard.de/vcppkold/constreferenzen.html

    ist irgendwie schlimm, wie sich der über eure einfache argumentation wie "-> ist nicht schön" oder "weil referenzen nicht 0 haben können, sind sie besser" hinwegsetzt. und das schon seit 5 jahren.

    Auch "der" muss ja nicht immer und überall Recht haben. 😉 😃 🤡



  • Man sollte nicht die Moeglichkeiten der NULL-Referenz unterschaetzen. Nehmen wir mal folgende Methode:

    MyObject *MyClass::GetCurrentReference (ErrorHandler *pErrorhandler = NULL)
    {
    if ((pCurrentReference == NULL) && (pErrorhandler != NULL))
    {
    pErrorhandler->WriteError ("No Reference Pointer\n");
    throw (ERROR_NO_REFERENCE); //irgentwo def.
    }

    return pCurrentReference;
    }

    Diese Zeigt so, ohne Ueberlagerung jeweils ein anderes Verhalten.



  • ich denke "der" ist ein nup.



  • Redhead schrieb:

    volkard schrieb:

    ich kenne da einen, der hat ne ganz komische meinung dazu.

    hier rät er ganz allgemein von referenzen als andere namen ab.
    http://www.volkard.de/vcppkold/referenzen.html

    hier rät er davon ab, referenzen als funktionsparameter zu nehmen.
    http://www.volkard.de/vcppkold/call_by_reference.html

    und hier sagt er, daß man aber const-referenzen als parameter ruhig und viel benutzen soll.
    http://www.volkard.de/vcppkold/constreferenzen.html

    ist irgendwie schlimm, wie sich der über eure einfache argumentation wie "-> ist nicht schön" oder "weil referenzen nicht 0 haben können, sind sie besser" hinwegsetzt. und das schon seit 5 jahren.

    Auch "der" muss ja nicht immer und überall Recht haben. 😉 😃 🤡

    Nein sicher nicht. Bringt aber im Gegensatz zu den anderen Argumente.

    mfg
    v R



  • virtuell Realisticer schrieb:

    Redhead schrieb:

    volkard schrieb:

    ich kenne da einen, der hat ne ganz komische meinung dazu.

    hier rät er ganz allgemein von referenzen als andere namen ab.
    http://www.volkard.de/vcppkold/referenzen.html

    hier rät er davon ab, referenzen als funktionsparameter zu nehmen.
    http://www.volkard.de/vcppkold/call_by_reference.html

    und hier sagt er, daß man aber const-referenzen als parameter ruhig und viel benutzen soll.
    http://www.volkard.de/vcppkold/constreferenzen.html

    ist irgendwie schlimm, wie sich der über eure einfache argumentation wie "-> ist nicht schön" oder "weil referenzen nicht 0 haben können, sind sie besser" hinwegsetzt. und das schon seit 5 jahren.

    Auch "der" muss ja nicht immer und überall Recht haben. 😉 😃 🤡

    Nein sicher nicht. Bringt aber im Gegensatz zu den anderen Argumente.

    mfg
    v R

    Da seine Argumente, für mich, nicht nachvollziehbar sind, sind das somit,
    für mich, auch keine Argumente. 😉


Anmelden zum Antworten