Wann nehme ich Objekt, Zeiger oder Referez?
-
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 mitvector<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.htmlhier rät er davon ab, referenzen als funktionsparameter zu nehmen.
http://www.volkard.de/vcppkold/call_by_reference.htmlund hier sagt er, daß man aber const-referenzen als parameter ruhig und viel benutzen soll.
http://www.volkard.de/vcppkold/constreferenzen.htmlist 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.htmlhier rät er davon ab, referenzen als funktionsparameter zu nehmen.
http://www.volkard.de/vcppkold/call_by_reference.htmlund hier sagt er, daß man aber const-referenzen als parameter ruhig und viel benutzen soll.
http://www.volkard.de/vcppkold/constreferenzen.htmlist 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.htmlhier rät er davon ab, referenzen als funktionsparameter zu nehmen.
http://www.volkard.de/vcppkold/call_by_reference.htmlund hier sagt er, daß man aber const-referenzen als parameter ruhig und viel benutzen soll.
http://www.volkard.de/vcppkold/constreferenzen.htmlist 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.htmlhier rät er davon ab, referenzen als funktionsparameter zu nehmen.
http://www.volkard.de/vcppkold/call_by_reference.htmlund hier sagt er, daß man aber const-referenzen als parameter ruhig und viel benutzen soll.
http://www.volkard.de/vcppkold/constreferenzen.htmlist 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 RDa seine Argumente, für mich, nicht nachvollziehbar sind, sind das somit,
für mich, auch keine Argumente.
-
gasi schrieb:
Werner Salomon schrieb:
CMyClass a; langFunc( boost::bind( &CMyClass::Meth1, &a, usw. ), .. weitere Parameter );
warum soll man hier boost::bind verwenden?
Muss man nicht. Das ist nur ein Beispiel. Der Funktor ist eine Möglichkeit die Schnittstelle zwischen der Klasse CMyClass und der Funktion langFunc zu reduzieren.
-
www.spam.geloescht
-
langFunc( boost::bind( &CMyClass::Meth1, &a, usw. ), .. weitere Parameter );
was ist hier der input an boost::bind und was das output von boost::bind...zugleich der input an langFunc??!?
-
gasi schrieb:
langFunc( boost::bind( &CMyClass::Meth1, &a, usw. ), .. weitere Parameter );
was ist hier der input an boost::bind und was das output von boost::bind...zugleich der input an langFunc??!?
Angenommen die Klasse CMyClass hat eine Methode Meth1, die einen int als Parameter aufnimmt.
CMyClass::Meth1( int x );
mit einem Aufruf von Meth1 wird das Objekt der Klasse CMyClass verändert, und genau das soll irgendwo innerhalb von langFunc geschehen. Dann gibt's die Möglichkeit, der Funktion langFunc eine Referenz oder einen Pointer auf das Objekt der Klasse CMyClass mitzugeben und langFunc ruft dann die Methode auf.
void langFunc( CMyClass& a, ..Rest interesiert hier nicht ) { // bla bla a.Meth1( x ); // <- hier wird's aufgerufen
Damit ist die 'formale' Schnittstelle zwischen langFunc und CMyClass relativ breit. Der Aufrufer weiß gar nicht so recht, was da innerhalb von langFunc mit 'a' passiert.
Nun gibt es die Möglichkeit, diesen Zugriff mittels eines Funktors (Foo s.u.) zu beschränken. Ausgeschrieben kann das so aussehen.struct Foo { explicit Foo( CMyClass& a ) : m_a( a ) {} void operator()( int x ) { m_a.Meth1( x ); } private: CMyClass& m_a; }; // der Aufruf von langFunc passiert jetzt mit CMyClass a; langFunc( Foo( a ), ... ); // dem Foo-Objekt wird eine Referenz von a mitgegeben // und innerhalb des (jetzt als Template codierten) langFunc geschieht template< typename T > void langFunc( T f, ..Rest interesiert hier nicht ) { // bla bla f( x ); // <- hier wird a.Meth1( x ) aufgerufen
Und damit man das alles (struct Foo s.o.) nicht selbst hinschreiben muss, kann man sich das von boost::bind generieren lassen. Der Ausdruck
boost::bind( &CMyClass:.Meth1, &a, _1 ); // _1 steht als Platzhalter für den Parameter
generiert genau so ein Funktor-Objekt wie Foo mit der Signatur void(*)( int ); dieser Funktor wird in langFunc aufgerufen und damit indirekt wieder die Methode Meth1 des Objekts 'a'. Der ganze Aufwand dient wie gesagt dazu, die Schnittstelle zu reduzieren und damit z.B. ganz konkret auch die Möglichkeit zu haben, von einem Objekt einer ganz anderen Klasse eine ganz andere Methode innerhalb von langFunc aufzurufen, und das ohne an langFunc eine Zeile zu ändern.
.. Verwirrung perfekt
?
Werner
-
Hmm gut, was nun - nur reine Geschmacksache?
Ich verstehe das jetz so: Uebergibt man bei pass-by-reference nur eine Reference, sollte man diese als Funktionsparameter auch mit const deklarieren und hat dann eben auch die dementsprechenden Eigenschaften. Stoeren diese "const-Eigenschaften", sollte man stattdessen die Adresse uebergeben, muss aber eine etwaige Behandlung fuer null implementieren. Hab ich das richtig verstanden?
Dann stellt sich fuer mich die Frage, ob es eben nicht auch Situationen/Faelle gibt, in denen eine Uebergabe MIT einer Reference OHNE const sinnvoll ist?
returnTyp function( paramTyp ¶m)
statt
returnTyp function( const paramTyp ¶m)
-
[quote="Corcovado"]Hmm gut, was nun - nur reine Geschmacksache?[quote]
jo.
das problem ist, daß wegen der gut funktionierenden RVO es kaum noch fälle gibt, wo man nicht-const-referenzen oder zeiger als funktionsparameter benötigen würde. so richtig wenige fälle. eigentlich so wenige fälle, daß es egal ist.mir fällt folgender ein:
template<typename T> class Lock{ NOCOPY; private: T& object; public: Lock(T& o): object(o){ o.lock(); } ~Lock(){ o.unlock(); } };
hier fühlt sich ne referenz irgendwie gescheiter an.
würde ich einen vector füllen wollen und nicht auf RVO hoffen können, würde ich wohl einen zeiger nehmen.
und nicht zu vergessen den lokale-referenz-trick:
void collectThingies(vector<int>* result){ assert(result!=0); vector<int>& r=*result; for(int i=0;i<100;++i) r.push_back(i*i%101); sort(r.begin(),r.end()); }
aber es kommt zu selten vor, ich habe keine meinung mehr zu diesem punkt.