Überaldung von "="
-
Hi Leute!
Ich wollte heute mal das = überladen, musste aber dann in meinem Buch lesen, dass das Überladen von = nur in Klassen definiert werden darf. Weiter schreibt mein Buch dazu nichts. Ich verstehe allerdings nicht so ganz was mit "darf nur in Klassen definiert werden" gemeint ist. Ich hab hier mal Code produziert der aber nicht so ganz tut was ich will:
#include<iostream> #include"test.h" int main() { INT a, b, c; a = 5; return 0; }#ifndef TEST_H #define TEST_H class INT { private: double val; public: INT() : val(0) { } INT(double v) : val(v) { } INT& operator = (INT &v) { val = v.val; return *this; } INT& operator + (INT &v) { val = val * v.val; return *this; } }; #endif TEST_HIch weiß das überladen von + zu * ist sinnlos; es soll ja auch nur eine Übungsaufgabe sein! Primär geht es mir darum zu verstehen, wie ich = im Sinne einer Linkszweisung überladen kann!
-
An sich schon fast richtig, bloß const-correctness nicht beachtet:
INT& operator = (const INT &v) { val = v.val; return *this; }P.S.: Dein Additionsoperator sollte besser einen INT, keinen INT& zurückgeben, das vermeidet ein paar komische Effekte.
Deine beiden Konstruktoren könntest du durch einen Defaultwert zu einem zusammenfassen. Ist double als Konstruktorargumentstyp nicht ein bisschen komisch für einen Integerersatz?
Derzeit macht dein Zuweisungsoperator übrigens das gleiche wie der Defaultzuweisungsoperator (es wird bei Bedarf nämlich automatisch einer erstellt, wenn man keinen angibt).
-
Das ganze nennt man Zuweisungsoperator. Du kannst es nur für Klassen verwenden, da dies fast die selbe Funktionsweiße ist wie du sie im Kopierkonstruktor findest. Du kannst damit das Kopierverhalten deiner Kasse selbst in die Hand nehmen. Wenn du den Operator nicht selbst explizit implementierst, wird dir dein Compiler einen standartmäßigen Zuweisungsoperator erstellen. Dieser kopiert dann alle Datenelemente deiner Klasse.
INT& operator = ( const INT& rhs ) { this->x = rhs.x; return *this; }MfG
Edit: SeppJ war mal wieder zu schnell...

-
Damit meint dein Buch wahrscheinlich, dass oprator= nur eine Elementfunktion sein kann, keine freie Funktion.
Einen solchen Operator musst du aber in diesem Fall auch nicht selbst deklarieren oder definieren. operator= gehört zu den sogenannten "special member functions", die ggf automatisch generiert werden.
Ein Tipp noch bei binären Operatoren wie +, -, , /: Vermeide die Veränderung von Operanden; denn das würde keiner erwarten, dass bei c=a+b plötzlich a verändert wird. operator+ liefert auch normalerweise keine Referenz, sondern ein neues Objekt. Du hast hier operator+ so definiert, wie man eigentlich operator= implementiert. Außerdem solltest du solche Operatoren lieber als freie Funktionen implementieren. Das hätte hier dann den Vorteil, dass 23+Int(42) auch funktionierte, statt nur Int(42)+23.
-
@SeppJ: Warum muss man hier const verwenden und was bewirkt const hier?
Schon mal Danke für die vielen hilfreichen Antworten!
-
vip@r schrieb:
@SeppJ: Warum muss man hier const verwenden und was bewirkt const hier?
Weil du sonst der Funktion keine temporären (oder allgemein konstante) Werte per Referenz übergeben kannst. Hier hast du einen solchen Wert vorliegen, denn bei
a = 5;steht links ein INT, rechts ein int. Einen
operator=(INT, int)hast du aber nirgends definiert. Aber es gibt ja noch den KonstruktorINT(double v), so dass der int über die Konvertierung nach double in einen INT konvertiert werden kann. Das dabei entstehende Objekt ist aber ein temporay vom Typ INT und kann nicht verändert werden. Die ursprüngliche FunktionssignaturINT& operator = (INT &v)sagt aber gerade aus, dass auch das v sich verändert (bzw. verändern kann), das passt daher nicht zusammen.
-
Danke SeppJ für die ausführliche Antwort!
Ich hab jetzt mal versucht für einen eigenen Datentypen Vektor den output-Stream zu überladen. Leider funktioniert das noch nicht so ganz. Der Compiler spuckt mir einige Fehler aus. Hier der Code:
Deklaration in der Klasse:
friend ostream &operator << (ostream &output, const Vector &v);Implementierung:
ostream& Vector::operator << (ostream &output, const Vector &v) { output << "(" << v.x << "," << v.y << ")" << std::endl; return output; }Fehlermeldungen:
Fehler 1 error C2143: Syntaxfehler: Es fehlt ';' vor '&' Fehler 2 error C2433: 'ostream': 'friend' bei der Deklaration von Daten nicht zulässig Fehler 3 error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt. Fehler 4 error C2061: Syntaxfehler: Bezeichner 'ostream' Fehler 5 error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt. Fehler 6 error C2805: Binärer Operator '<<' hat zu wenig Parameter Fehler 7 error C2143: Syntaxfehler: Es fehlt ';' vor '&' Fehler 8 error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt. Fehler 9 error C2086: 'int ostream': Neudefinition Fehler 10 error C2065: 'output': nichtdeklarierter Bezeichner Fehler 11 error C2059: Syntaxfehler: 'const' Fehler 12 error C2143: Syntaxfehler: Es fehlt ';' vor '{' Fehler 13 error C2447: '{': Funktionsheader fehlt - Parameterliste im alten Stil? Fehler 14 error C2143: Syntaxfehler: Es fehlt ';' vor '&' Fehler 15 error C2433: 'ostream': 'friend' bei der Deklaration von Daten nicht zulässig Fehler 16 error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt. Fehler 17 error C2061: Syntaxfehler: Bezeichner 'ostream' Fehler 18 error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt. Fehler 19 error C2805: Binärer Operator '<<' hat zu wenig Parameter
-
Bitte mal dritten Link in meiner Signatur lesen. Spekulation wäre alles von vergessenem Semikolon zu vergessener Auflösung des Namespaces std. Das bringt nichts, wild herum zu raten.
-
friend ostream &operator << (ostream &output, const Vector &v); ostream& operator << (ostream &output, const Vector &v) { return output << "(" << v.x << "," << v.y << ")" << std::endl; }Das ist keine Memberfunktion der Klasse. So dachte dein Compiler an den shift operator.
-
Also:
Benutzte IDE: Visual Studio 2010
Benutzter Compiler: Der, der in VS2010 eingebaut ist.Ich hab in verteilten Dateien gearbeitet. Ich kopiere jetzt alles für mich relevant erschienene hier rein:
Header-Datei:
#ifndef TEST_H #define TEST_H class Vector { private: double x; double y; double skalar; public: Vector(); Vector(double _x, double _y); Vector &operator + (const Vector vec); Vector &operator - (const Vector vec); double &operator * (const Vector vec); friend ostream &operator << (ostream &output, const Vector &v); }; #endif TEST_HImplementierungsdatei:
#include<iostream> #include"test.h" //using namespace std; Vector::Vector() : x(0), y(0) { } Vector::Vector(double _x, double _y) : x(_x), y(_y) { } Vector& Vector::operator + (const Vector vec) { x = x + vec.x; y = y + vec.y; return *this; } Vector& Vector::operator - (const Vector vec) { x = x - vec.x; y = y - vec.y; return *this; } double& Vector::operator * (const Vector vec) { x = x * vec.y; y = y * vec.y; skalar = x + y; return skalar; } ostream& Vector::operator << (ostream &output, const Vector &v) { output << "(" << v.x << "," << v.y << ")" << std::endl; return output; }Die main-Datei hat keine Auswirkung auf die Fehler! Egal ob ich Vektoren deklariere oder nicht, ob ich sie schon ausgeben lasse, oder nicht. Alles egal. Deshalb lass ich die main mal weg. Ich hoff, jetzt entspricht es dem Standard hier im Board.
-
std::ostreamDazu brauchst du noch entweder den Header
iosfwd,ostreamoder gleichiostream.
-
Den header <iostream> hab ich aber in der Implementierungsdate schon eingebunden, oder meinst du mit deiner letzten Antwort, dass ich den in der Klassendatei auch noch einbinden soll?
klasse:
friend std::ostream &operator << (std::ostream &output, const Vector &v);Implementierung:
std::ostream& Vector::operator << (std::ostream &output, const Vector &v) { output << "(" << v.x << "," << v.y << ")" << std::endl; return output; }So siehts nach den veränderungenaus. alles andere ist so geblieben. Fehlermeldung:Fehler 1 error C2039: '<<': Ist kein Element von 'Vector'
-
vip@r schrieb:
Header-Datei:
#ifndef TEST_H #define TEST_H class Vector { private: double x; double y; double skalar; public: Vector(); Vector(double _x, double _y); Vector &operator + (const Vector vec); Vector &operator - (const Vector vec); double &operator * (const Vector vec); friend ostream &operator << (ostream &output, const Vector &v); };
Du hast wieder ? mit ?= verwelchselt, diesmal im Falle +, - und * anstelle des Fragezeichens.int a = 23; int b = 42; a+b; //verändert a nicht!!!Im Wesentlichen hast du den letzten Absatz meines vorherigen Beitrags komplett ignoriert.
-
Ich wollte mit dieser *neuen* Klasse die Überladung der Operatoren nicht implementieren. Ich hab deinen letzten Absatz deiner Antwort sehr wohl gelesen und deine Tips im Projekt berücksichtigt.
-
Bei der Implementation von
ostream& operator <<()mußt DuVector::weglassen, da sie kein Member vonVectorist. (Hat Nymer schon mal erwähnt)
-
@Krümelkacker: Hier mal nochmal die Variante die du mir vorgeschlagen hast. Ich hoffe, dass das dem entspricht, was du mir beibringen wolltest:
#ifndef TEST_H #define TEST_H class Vector { private: double x; double y; double skalar; public: Vector(); Vector(double _x, double _y); Vector &operator += (const Vector vec); Vector &operator + (const Vector vec); Vector &operator -= (const Vector vec); Vector &operator - (const Vector vec); double &operator * (const Vector vec); friend std::ostream &operator << (std::ostream &output, const Vector &v); }; #endif TEST_H#include<iostream> #include"test.h" Vector::Vector() : x(0), y(0) { } Vector::Vector(double _x, double _y) : x(_x), y(_y) { } Vector& Vector::operator += (const Vector vec) { x = x + vec.x; y = y + vec.y; return *this; } Vector& Vector::operator + (const Vector vec) { Vector tmp(x, y); return tmp += vec; } Vector& Vector::operator -= (const Vector vec) { x = x - vec.x; y = y - vec.y; return *this; } Vector& Vector::operator - (const Vector vec) { Vector tmp(x, y); return tmp -= vec; } double& Vector::operator * (const Vector vec) { x = x * vec.y; y = y * vec.y; skalar = x + y; return skalar; } std::ostream& operator << (std::ostream &output, const Vector &v) { output << "(" << v.x << "," << v.y << ")" << std::endl; return output; }Danke Nymer, dass du mich nochmal drauf aufmerksam gemacht hast! Jetzt funktioniert übrigens die gesamte Klasse! Danke an euch. Das hat mir sehr weiter geholfen! Über eine Rückmeldung von Krümelkacker würde ich mich sehr freun, ob das dem entspricht, was er mir beibringen wollte

-
Du kannst übrigens direkt:
return output << "(" << v.x << "," << v.y << ")" << std::endl;schreiben anstatt:
output << "(" << v.x << "," << v.y << ")" << std::endl; return output;, was den gleichen Effekt hat.
Ich persönlich finde das schöner
.
-
vip@r schrieb:
@Krümelkacker: Hier mal nochmal die Variante die du mir vorgeschlagen hast. Ich hoffe, dass das dem entspricht, was du mir beibringen wolltest:
Du gibst bei operator+ und co Referenzen auf temporäre Objekte zurück, die dann ungültig werden. Bei operator+ und co fehlt noch ein
constam Ende der Deklarationen. Die Parameter würde ich als Referenz-auf-konstant entgegen nehmen. Operator verändert das Objekt auf der linken Seite und gibt eine Referenz auf ein Datenelement der linken Seite zurück*. Das ist eine extrem schlechte Idee für einen binären Operator, der normalerweise seine Operanden nicht verändert und nur einen neuen Wert zurück gibt.Nur mal so ein Beispiel:
Vector& Vector::operator + (const Vector vec) { Vector tmp(x, y); return tmp += vec; }hätte lieber
Vector Vector::operator + (const Vector& vec) const // <- const nicht vergessen! { // ^ keine Referenz!! ^ Referenz!! Vector tmp(x, y); tmp += vec; return tmp; // <-- so aufgeschrieben erlaubt es noch die NRVO-Optimierung }heißen sollen. Allerdings kannst du es auch so schreiben:
Vector Vector::operator + (const Vector& vec) const { return Vector(x+vec.x,y+vec.y); }wobei ich natürlich nicht weiß, was das mit deinem
skalar-Member da soll. Der wird ja nicht mal irgendwo initialisiert!Prinzipiell würde ich operator+, operator-, operator* aus "Symmetriegründen" auch als freie Funktionen definieren. Was das genau heißt, hatte ich letztens schon angedeutet.
Du musst übrigens nicht das Rad neu erfinden. Es gibt beispielsweise eine nette Matrix/Vektor-Bibliothek mit diversen netten Funktionen: Eigen.
-
krümelkacker schrieb:
Prinzipiell würde ich operator+, operator-, operator* aus "Symmetriegründen" auch als freie Funktionen definieren. Was das genau heißt, hatte ich letztens schon angedeutet.
Das würde ich unbedingt machen und so macht man es afaik auch.
Wenn du z.B. noch multiplizieren mit einem Skalar hinzufügst, geht das so nicht (V*s) geht (s*V) nicht.Du musst übrigens nicht das Rad neu erfinden. Es gibt beispielsweise eine nette Matrix/Vektor-Bibliothek mit diversen netten Funktionen: Eigen.
Eigen ist toll. Einfach inkludieren und fertig.
Vielleicht will er aber auch einfach operatoren Überladung üben.
-
Danke Leute, für eure tollen Beispiele und Hilfe!
Ja, ich will in der Tat die Operatorüberladung üben. Ich werde mir so gut es geht mir eure Tips zu herzen nehmen! Ich hab hier nun übrigens wieder ein Problemchen mit einer Klasse und einer Überladung. Verwendete IDE und Compiler: VS2010.
Header:
class Bitvector { private: unsigned int bits; //Anzahl der Bits im Bitvektor unsigned char byte; //chars aus denen sich der Bitvektor zusammensetzt unsigned char* bv; //pointer auf die aneinandergereihten chars public: Bitvector(); Bitvector(unsigned int bits); ~Bitvector(); friend std::ostream& operator << (std::ostream& output, Bitvector &bv); };Implementierung:
std::ostream& operator << (std::ostream &output, const Bitvector &bv) { bv.bits; //hier gibts den Fehler! return output; }Compilerfehler:
Fehler 1 error C2248: "Bitvector::bits": Kein Zugriff auf private Member, dessen Deklaration in der Bitvector-Klasse erfolgte.Ich brauch in der Überladung Zugriff auf die "bits" der Klasse. Wie mach ich das dann hier? Ich hab eigentlich gedacht, dass ich hier ohne Probleme mein Wissen von gester anwenden kann, aber schon kommt wieder was das mich raushaut... Könnt ihr mir helfen?
Edit:
Wenn ich den Zugriffsspezifizierer private auf public ändere, bekomme ich hier Zugriff auf bits. Warum kann ich bei dieser Ausgabeoperator-Überladung nicht, wie in "normalen" Methoden üblich, auf private-Variablen zugreifen? Wie gehe ich dann hier vor? Muss ich eine Methode schreiben, die mir den Wert private-Var. liefert? Oder gibt es eine andere Möglichkeit?