Überladen von Operatoren
-
Dann zitier ich dich mal selbst:
wenn man sie (wie es sauberes programmieren ist) außerhalb der Klasse definiert.
Ansonsten haben dir auch schon genug Leute gesagt, dass der Operator als freie Funktion definiert werden soll und eben nicht in einer Klasse.
-
Man deklariert innerhalb der Klasse
und definiert außerhalb.Ist doch normal.....
Und genau so hab ichs gemacht,
sehe dir den letzten Post von mir an, da hab ich
mal den header gepostet, alles nur deklarationen
nix definiert.
-
Lybrial schrieb:
Man deklariert innerhalb der Klasse
und definiert außerhalb.Nein, eine Funktion ist entweder innerhalb einer Klasse deklariert und definiert oder außerhalb. Beides gibts nicht. Du hast die Definition der Funktion einfach nur räumlich getrennt von der Klassendefinition, aber die Signatur der Funktionsdefinition sagt ganz klar, dass die Funktion zur Klasse gehört.
class Foo { void bar(); } void Foo::bar() // bar gehört zu Foo { } void baz() // freie Funktion { }
-
Versteh ich jetzt nicht ganz, funktioniert es dann grundsätzlich nicht
wenn man das so macht oder ist das nur "unschön"Ein Beispiel das ich gemacht habe, eine Log-Klasse:
Header
#ifndef __LOGHANDLER #define __LOGHANDLER #include <stdio.h> #include "Singleton.hpp" enum LOGLEVEL { L_OK, L_ERROR, L_FATAL_ERROR, L_INFO L_WARN L_DEBUG }; class CLogHandler : public TSingleton<CLogHandler> { public: CLogHandler(); //constructor ~CLogHandler(); //destructor void createLogHandler(const char *LogName); //new logfile void message(const char *File, int Line, const char *Time, const char *msg, LOGLEVEL level); //writes message into logfile private: FILE *m_Logfile; //the FILE ;) const char* convertEnum(LOGLEVEL level); //to convert the enums ;) }; #endifdie cpp dazu, in der die Methoden definiert sind:
#include <iostream> #include <string.h> #include "LogHandler.hpp" using namespace std; //constructor CLogHandler::CLogHandler(){}; //destructor CLogHandler::~CLogHandler(){ message(__FILE__, __LINE__, __TIME__,"End of logfile!", L_INFO); fclose(m_Logfile); } //create logfile and write head void CLogHandler::createLogHandler(const char *LogName) { try { m_Logfile = fopen(LogName, "w");//fopen = fileopen, w = write if (m_Logfile == NULL) { throw "Datei konnte nicht geoeffnet werden!"; } } catch (char *ErrorString) { cout << "Fehler aufgetreten: " << ErrorString << endl; cout << "Programm wird beendet!" << endl; exit(0); } message(__FILE__, __LINE__, __TIME__, "THIS IS THE LOGFILE!!!", L_INFO); //print out actual build-configuration #ifdef _DEBUG message(__FILE__, __LINE__, __TIME__, "BUILD: DEBUG", L_INFO); #else message(__FILE__, __LINE__, __TIME__, "BUILD: RELEASE", L_INFO); #endif //close logfile and open it again with append fclose(m_Logfile); try { m_Logfile = fopen(LogName, "a");//fopen = fileopen, a = append if (m_Logfile == NULL) { throw "Datei konnte nicht geoeffnet werden!"; } } catch (char *ErrorString) { cout << "Fehler aufgetreten: " << ErrorString << endl; cout << "Programm wird beendet!" << endl; exit(0); } } //message method to print a message into the logfile with LOGLEVEL void CLogHandler::message(const char *File, int Line, const char *Time, const char *msg, LOGLEVEL level) { fprintf(m_Logfile,"[<%s> - <%s>] at: <%20s>: <%d> <%s>\n", convertEnum(level), Time, File, Line, msg); fflush(m_Logfile);//forces instantly writing into the file } const char* CLogHandler::convertEnum(LOGLEVEL level) { switch(level) { case L_OK: return "INFO "; break; case L_ERROR: return "ERROR"; break; case L_FATAL_ERROR: return "FATAL"; break; case L_INFO: return "INFO "; break; case L_WARN: return "WARN "; break; case L_DEBUG: return "DEBUG"; break; default: return "INFO "; break; } }und das funktioniert herrlich
-
Mal noch ein paar andere Sachen:
class lCVector3 {Schrecklicher Name. Was sollen l und C bedeuten?
struct { float x; float y; float z; }; // coordsWarum packst du das in ein struct? Warum benutzt du float statt double?
l lCVector3(const lCVector3 &v); // copyWofür musst du diesen Konstruktor explizit definieren? Danke an die Regel der großen Drei.
lCVector3(const float f); // normal each element, same numberWas soll "normal" bedeuten? Komischer Satz
lCVector3(const float *pfComponent); // normal array with three elementsSchlechter Stil. Übergib ein std::array, wenns denn unbedingt sein muss, Hauptsache du hast eine sinnvolle Größenüberprüfung.
// operation assignments lCVector3 operator = (const lCVector3 &v);Siehe Kommentar zum Copy-Konstruktor.
lCVector3 operator *= (const lCVector3 &v);Wie ist die Multiplikation von zwei Vektoren definiert?
lCVector3 operator /= (const lCVector3 &v);Wie ist die Division von zwei Vektoren definiert?
// arithmetical operations lCVector3 operator + (const lCVector3 &v); lCVector3 operator - (const lCVector3 &v); lCVector3 operator * (const lCVector3 &v); lCVector3 operator * (const float f); lCVector3 operator / (const lCVector3 &v); lCVector3 operator / (const float f); // compare operations bool operator == (const lCVector3 &v); bool operator != (const lCVector3 &v);Als freie Funktionen definieren.
// function declaration float lCVector3Length(const lCVector3 &v); float lCVector3LengthSq(const lCVector3 &v); lCVector3 lCVector3Normalize(const lCVector3 &v); lCVector3 lCVector3NormalizeEx(const lCVector3 &v);Schlechte Namen, was soll lCVector3? Warum kürzt du die wesentlichen Teile ab? Ich weiß nicht, was du mit NormaliseEx meinst.
lCVector3 lCVector3Cross(const lCVector3 &v1, const lCVector3 &v2); float lCVector3Point(const lCVector3 &v1, const lCVector3 &v2); float lCVector3Angle(const lCVector3 &v1, const lCVector3 &v2);Hier hast du wiederum drei Parameter, nämlich this, v1 und v2. Was ist das Kreuzprodukt dreier Vektoren? Point ist ein schlechter Name, den versteht man nur, weil Cross direkt drübersteht.
lCVector3 lCVector3InterpolateCoords(const lCVector3 &v1, const lCVector3 &v2, const float p); lCVector3 lCVector3InterpolateNormal(const lCVector3 &v1, const lCVector3 &v2, const float p);Hiermit dasselbe. Die Namen sind auch ziemlich nichtssagen.d
-
Lybrial schrieb:
Versteh ich jetzt nicht ganz, funktioniert es dann grundsätzlich nicht
wenn man das so macht oder ist das nur "unschön"Lies dir dazu bitte nochmal den entsprechenden Abschnitt in deinem Buch durch. Ich denke, wenn du dir meine Erklärung nochmal langsam durchliest, solltest du es auch so verstehen.
Ein Beispiel das ich gemacht habe, eine Log-Klasse:
Hier sind wieder so viele verbesserungswürdige Sachen drin... Wieso hast du eine Funktion createLogHandler, die die Aufgabe des Konstruktors übernimmt, während der Konstruktor gar nichts macht? Wieso erbst du von Singleton? Und so weiter, das geh ich nicht auch noch alles durch.
-
Danke für deine Tipps, sowas mag ich sehr gerne, mein ich ernst

Aber zur Log-Klasse:
Erbt von Singleton weil ich Singleton als Template Klasse schon implementiert hatte und meine Log-Klasse nur einmal exisitieren soll.
-
Lybrial schrieb:
DErbt von Singleton weil ich Singleton als Template Klasse schon implementiert hatte und meine Log-Klasse nur einmal exisitieren soll.
Und warum musst du im Code garantieren, dass nur eine Instanz erstellt werden kann? Deine Implementierung der Klasse passt übrigens gar nicht zum Singleton-Pattern.
-
Ok und warum nicht?
So lange bin ich noch nicht in C++ um alles zu wissen aber über Tips freue ich mich. (Genau gesagt bin ich seid zwei Wochen an C++)wenn ich das ganze so mache um zurück zu Vektor-Klasse zu kommen funzts:
Vector Vector::operator + (const Vector &v) { Vector *v1 = new Vector(x + v.x, y + v.y, z + v.z); return *v1; }
-
Lybrial schrieb:
wenn ich das ganze so mache um zurück zu Vektor-Klasse zu kommen funzts:
Vector Vector::operator + (const Vector &v) { Vector *v1 = new Vector(x + v.x, y + v.y, z + v.z); return *v1; }Und dies ist sehr schlechter Code (Ungeachtet davon das man weiterhin den operator+ nicht als Memberfunktion implementieren sollte). Wozu die Zeiger und das new?
Warum nicht gleich so:
Vector Vector::operator + (const Vector &v) { return Vector(x + v.x, y + v.y, z + v.z); }
-
Lybrial schrieb:
Ok und warum nicht?
Du stellst die falsche Frage. Du machst deinen Code durch das "Singleton" (Schau dir die Implementierung eines Singletons besser nochmal an. Danach vergisst du bitte seine Existenz und benutzt es nur noch in absoluten Ausnahmefällen.) komplizierter. Dafür musst du eine Rechtfertigung haben.
-
Ok, aber ist es nicht Rechtfertigung genug, das eine Instanz
der Log-Klasse nur einmal existieren soll. Schließlich überschreibe
ich meine Log-Datei jedes mal damit sie nicht eines Tages 20GB groß ist
(ja es gibt commerzielle programme die das machen)
Und wenn jetzt mehrere Teile eines Programms (Netzwerk, File-Handling,
usw. )in die Log schreiben, will ich, dass sie alle in die selbe Log schreiben.
-
Lybrial schrieb:
Ok, aber ist es nicht Rechtfertigung genug, das eine Instanz
der Log-Klasse nur einmal existieren soll.Eine Instanz existiert immer nur einmal. Meinst du, dass du nur eine Instanz der Klasse haben willst? Ein Singleton setzt man ein, wenn es schlimm wäre, dass es mehrere Instanzen der Klasse gibt. Warum ist das hier der Fall, selbst wenn du nur eine Instanz erstellen willst?
Schließlich überschreibe
ich meine Log-Datei jedes mal damit sie nicht eines Tages 20GB groß ist
(ja es gibt commerzielle programme die das machen)Ich sehe den Zusammenhang nicht.
Und wenn jetzt mehrere Teile eines Programms (Netzwerk, File-Handling,
usw. )in die Log schreiben, will ich, dass sie alle in die selbe Log schreiben.Dann sollen sie alle dieselbe Instanz benutzen.
-
Ich mach mir mal Gedanken dazu

Ich danke dir sehr für deine vielen Tipps, habe versucht
das meiste davon umzusetzen und die Vektor-Klasse funktioniert
jetzt wunderbar!!!