Disclaimer: Dieser Thread wurde aus dem alten Forum importiert. Daher werden eventuell nicht alle Formatierungen richtig angezeigt. Der ursprüngliche Thread beginnt im zweiten Post dieses Threads.
Call-by-value vs. Call-by-reference
Kann mir jemand erläutern, mit welchen Datentypen (Objekt oder Pointer) man es bei den folgenden Codeschnipseln zu tun hat?
void mult(const Complex &r);
Das ist doch Call-by-reference, richtig? Dann hätte ich angenommen, dass man mit → auf die Attribute/Methoden von r zugreift. Der Compiler will aber .
Ist es äquivalent, wenn man void mult(const Complex* r) schreibt und dann mit → auf die Attribute/Methode zugreift?
Im Prinzip ja.
Eine Referenz verhält sich zwar (ähnlich) wie ein Pointer (u.a. mit dem Unterschied, dass die Referenz immer gültig ist, im Gegensatz zum Pointer), wird aber in C++ nicht mit dem → Operator, sondern einfach mit dem . angesprochen. Das hat noch weitere Vorteile: Man kann z.B. die Signatur einer Methode von
[m]foo(Object o);[/m]
zu
[m]foo(const Object &o);[/m]
ändern, ohne dass im Allgemeinen innerhalb wie außerhalb der Methode der Code angepasst werden muss.
Wichtig bei dem Aufruf ist allerdings (und das ist der Unterschied), dass bei der Übergabe eines Pointers vor der Dereferenzierung geprüft werden muss, ob das Objekt überhaupt existiert (!= nullptr). Bei der Referenz kann man dies immer annehmen.
Anhand des Rückgabewerts sieht man, dass im ersten Fall ein schon bestehendes Objekt verändert werden soll (deswegen Referenz). Dies kann man sich ganz gut erklären, wenn man sich den Operator für primitive Datentypen ansieht:
[m][color=black]int a = 5, b = 3;
a += b;[/color][/m]
Im zweiten Fall würde man ein neues Objekt haben wollen.
Heisst das, bei Complex& Complex::operator-() soll man ein bestehendes Objekt verändern?
Ich hab mir das eher wie a = -b vorgestellt, da wird der Inhalt von b ja nicht verändert.
Vielleicht sollte man aus gegebenem Anlass noch erwähnen, dass dies wirklich mal so von der Headerdatei des aktuellen Aufgabenblattes gefordert war, bis es nach einigen Korrektur-Uploads jetzt auf [m]Complex Complex::operator-()[/m] geändert wurde.
Das ist auch gut so, denn dein Beispiel ist nicht die übliche Variante, diesen Operator zu implementieren.
Denn wie du schon festgestellt hast, wird in Implementierungen dieses Operators bei anderen Datentypen auch ein neues Objekt erstellt, wie BTL im Post über mir erklärt hat.
Denn bei…
int a = 42;
int b = -a;
…wird a ja auch nicht geändert und ist nach Zeile 2 immer noch 42 und nicht -42.
–
Es wurde außerdem eine Änderung der Beschreibung zu [m]void Complex::conj() [/m]vorgenommen. Aus dem ursprünglichen kompletten Negieren der komplexen Zahl wurde jetzt die semantisch richtige komplexe Konjugation (also nur den Imaginärteil negieren). Demnach ist jetzt auch [m]conj()[/m] nicht mehr gleich dem [m]operator-[/m].
Sollte man vielleicht beachten, falls man noch nicht die aktuelle Version heruntergeladen hat.
Ah. Und steht das auch irgendwo auf deren Seite, dass sie die Angabe verändert haben? Konnte da jetzt nichts finden. Und das wäre schon irgendwie ganz praktisch.
Die Kunst besteht darin, die Aufgabenstellung falsch hochzuladen, obwohl es dieselbe Aufgabe ist wie in den vergangenen Jahren - wo sie sogar richtig war.[/nörgeln]
Das AlgoKS-Team hat sich wohl unter anderem zum Ziel gesetzt zu einem hohen Maße für “erwarte das Unerwartete” zu sensibilisieren. Plan geht imo ganz gut auf.
Mit Referenzen würde das nicht klappen.
Grundsätzlich empfiehlt es sich so oft wie möglich konstante (nur wenn absolut notwendig, sollten sie nicht konstant sein) Referenzen zu verwenden. Konstante Pointer sind vor allem dann sinnvoll, wenn auch kein Wert (Nullpointer) ein gültiger Parameter ist.
Deine Aussagen über const-correctness stimmen soweit, allerdings ist der [m]nullptr[/m] selbst imho ein Keyword (welches ein Literal vom Typ std::nullpointer_t repräsentiert).
Da aktualisiert man seine zuvor unveraenderte complex.h und danach schauts so aus:
$ make
g++ -Wall -g -DLINUX -std=c++11 -o A1 main.cpp complex.cpp matrix.cpp
complex.cpp: In constructor ‘Complex::Complex()’:
complex.cpp:7:22: error: class ‘Complex’ does not have any field named ‘m_real’
complex.cpp:7:33: error: class ‘Complex’ does not have any field named ‘m_imag’
complex.cpp: In constructor ‘Complex::Complex(float, float)’:
complex.cpp:13:44: error: class ‘Complex’ does not have any field named ‘m_real’
complex.cpp:13:58: error: class ‘Complex’ does not have any field named ‘m_imag’
complex.cpp: In member function ‘float Complex::getReal() const’: pts/1 1116
complex.cpp:16:9: error: ‘m_real’ was not declared in this scope
complex.cpp: In member function ‘float Complex::getImag() const’:
complex.cpp:20:9: error: ‘m_imag’ was not declared in this scope
complex.cpp: In member function ‘void Complex::setReal(float)’:
complex.cpp:24:2: error: ‘m_real’ was not declared in this scope
complex.cpp: In member function ‘void Complex::setImag(float)’:
complex.cpp:28:2: error: ‘m_imag’ was not declared in this scope
complex.cpp: In member function ‘void Complex::print()’:
complex.cpp:32:15: error: ‘m_real’ was not declared in this scope
complex.cpp:32:34: error: ‘m_imag’ was not declared in this scope
complex.cpp: In member function ‘void Complex::add(const Complex&)’:
complex.cpp:37:2: error: ‘m_real’ was not declared in this scope
complex.cpp:38:2: error: ‘m_imag’ was not declared in this scope
complex.cpp: In member function ‘void Complex::sub(const Complex&)’:
complex.cpp:42:2: error: ‘m_real’ was not declared in this scope
complex.cpp:43:2: error: ‘m_imag’ was not declared in this scope
complex.cpp: In member function ‘void Complex::mult(const Complex&)’:
complex.cpp:47:12: error: ‘m_real’ was not declared in this scope
complex.cpp:48:12: error: ‘m_imag’ was not declared in this scope
complex.cpp: In member function ‘void Complex::div(const Complex&)’:
complex.cpp:56:12: error: ‘m_real’ was not declared in this scope
complex.cpp:57:12: error: ‘m_imag’ was not declared in this scope
complex.cpp: In member function ‘void Complex::conj()’:
complex.cpp:66:2: error: ‘m_imag’ was not declared in this scope
complex.cpp: At global scope:
complex.cpp:89:10: error: prototype for ‘Complex& Complex::operator-()’ does not match any in class ‘Complex’
In file included from complex.cpp:1:0:
complex.h:60:10: error: candidates are: Complex Complex::operator-(const Complex&)
complex.h:57:10: error: Complex Complex::operator-()
complex.cpp: In member function ‘Complex Complex::operator+(const Complex&)’:
complex.cpp:95:22: error: ‘m_real’ was not declared in this scope
complex.cpp:95:30: error: ‘m_imag’ was not declared in this scope
complex.cpp: In member function ‘Complex Complex::operator-(const Complex&)’:
complex.cpp:101:22: error: ‘m_real’ was not declared in this scope
complex.cpp:101:30: error: ‘m_imag’ was not declared in this scope
complex.cpp: In member function ‘Complex Complex::operator*(const Complex&)’:
complex.cpp:107:22: error: ‘m_real’ was not declared in this scope
complex.cpp:107:30: error: ‘m_imag’ was not declared in this scope
complex.cpp: In member function ‘Complex Complex::operator/(const Complex&)’:
complex.cpp:113:22: error: ‘m_real’ was not declared in this scope
complex.cpp:113:30: error: ‘m_imag’ was not declared in this scope
complex.cpp: In member function ‘bool Complex::operator==(const Complex&)’:
complex.cpp:119:9: error: ‘m_real’ was not declared in this scope
complex.cpp:119:34: error: ‘m_imag’ was not declared in this scope
complex.cpp:120:1: warning: control reaches end of non-void function [-Wreturn-type]
complex.cpp: In member function ‘float Complex::getImag() const’:
complex.cpp:21:1: warning: control reaches end of non-void function [-Wreturn-type]
complex.cpp: In member function ‘float Complex::getReal() const’:
complex.cpp:17:1: warning: control reaches end of non-void function [-Wreturn-type]
make: *** [A1] Error 1
Auf der Seite steht natuerlich nix von Aenderungen…
[m]git diff[/m]
Ahh
jetzt ist auch valgrind zufrieden…