PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : operator<< überladen


Che
2003-07-19, 11:39:04
Hi,
Ich möchte den operator<< für meinie Log Klasse überladen. Im Grunde funktionierts ja schon:

friend CLog& operator<< (CLog& logclass, const char *text);

Nun müsste ich aber für jeden Typ (int, float, double, wasweisich) den oparator neu überladen:

friend CLog& operator<< (CLog& logclass, const int x);
friend CLog& operator<< (CLog& logclass, const unsigned int x);
friend CLog& operator<< (CLog& logclass, const float x);
friend CLog& operator<< (CLog& logclass, const wasweisich x);


Und wie siehts mit Manipulators à la aus? Irgendwie muss es doch möglich sein direkt die Methoden der Stream Klassen dafür zu benutzen, sodass man nicht den ganzen Code neu schreiben muss.

Irgendwelche Ideen?

MfG

Xmas
2003-07-19, 13:24:19
Original geschrieben von Che
Hi,
Ich möchte den operator<< für meinie Log Klasse überladen. Im Grunde funktionierts ja schon:

friend CLog& operator<< (CLog& logclass, const char *text);

Nun müsste ich aber für jeden Typ (int, float, double, wasweisich) den oparator neu überladen:

friend CLog& operator<< (CLog& logclass, const int x);
friend CLog& operator<< (CLog& logclass, const unsigned int x);
friend CLog& operator<< (CLog& logclass, const float x);
friend CLog& operator<< (CLog& logclass, const wasweisich x);


Und wie siehts mit Manipulators à la aus? Irgendwie muss es doch möglich sein direkt die Methoden der Stream Klassen dafür zu benutzen, sodass man nicht den ganzen Code neu schreiben muss.

Irgendwelche Ideen?

MfG
Wieso eigentlich friend und keine Memberfunktion?
Wenn der Code für alle Typen gleich ist, kannst du auch ein Template schreiben. Ansonsten führt kein Weg daran vorbei den Operator für jeden benötigten Typ zu überladen. Aber das sollte wohl ein sehr geringer Aufwand sein.

Manipulators á la?

Che
2003-07-19, 13:55:57
operator<< muss anscheinend eine externe Funktion sein, ansonsten bekomme ich einen Error ich hätte zuviele Argumente verwendet.

binary 'operator operator' has too many parameters

The specified overloaded binary operator was declared with more than one parameter.

The following is an example of this error:

class X
{
X operator+ ( X , X ); // error, two parameters
X operator+ ( X ); // OK, one parameter
};



Manipulators à la endl oder flush. Ist wohl irgendwie untergegangen...:D

Xmas
2003-07-19, 15:28:04
Original geschrieben von Che
operator<< muss anscheinend eine externe Funktion sein, ansonsten bekomme ich einen Error ich hätte zuviele Argumente verwendet.

Das Beispiel dazu hast du dir auch genau angeschaut?

Wenn du einen binären Operator (zwei Operanden) überladen willst, gibt es zwei Varianten:

friend CLog& operator<< (CLog& logclass, const char *text);
CLog& operator<< (const char *text);

Bei einer Memberfunkion musst du logischerweise keine Instanz als Parameter übergeben, weil du die Memberfunktion ja nur in Verbindung mit einer Instanz der Klasse aufrufen kannst.

friend brauchst du nur, wenn der Operand auf der linken Seite des Operators nicht Instanz der Klasse ist, die du gerade definierst. Also wenn du z.B. eine Vektorklasse schreibst, und du sowohl vektor * int (Memberfunktion, 1 Parameter) als auch int * vektor (Friendfunktion, 2 Parameter) rechnen willst.

Ist der Operator eine Memberfunktion, so ist z.B. folgendes identisch:

a << b;
a.operator<<(b);



Manipulators à la endl oder flush. Ist wohl irgendwie untergegangen...:D
Was macht deine Log-Klasse eigentlich anders als die Standard Streams? Vielleicht kannst du ja einfach die Funktionalität erben?

Che
2003-07-19, 18:07:06
Du hast wie immer recht. :)

Ich beschäftige mich zum ersten mal etwas näher mit Vererbung, Überladung und diesem ganzen wunderbaren C++ Zeug, man möge mir meine Fehler verzeihen. (Aber dass ich bei der Member-Variante eine Instanz des Objekts übergeben wollte ist ziemlich unverzeihlich :bonk: )

Jetzt habe ich ein neues Problem: Wie gebe ich das Objekt zurück? Bei folgendem Prototyp
CLog& operator<< (const char *text);
erzeugt
return this; einen Fehler.


Von der Standard Stream Klasse erben? Das ist ja frech ;)

Xmas
2003-07-19, 18:18:32
Original geschrieben von Che
Jetzt habe ich ein neues Problem: Wie gebe ich das Objekt zurück? Bei folgendem Prototyp
CLog& operator<< (const char *text);
erzeugt
return this; einen Fehler.

return *this;;)

Die Verwendung von Referenztypen verwirrt anfangs etwas, weil sie intern ja eigentlich Pointer sind, aber syntaktisch eben wie die Typen, auf die sie verweisen, gehandhabt werden.

Du kannst einem Referenztypen keinen Pointer zuweisen, nur ein entsprechendes Objekt.

Che
2003-07-19, 18:38:54
Also nochmal langsam damit ich mitkomme: Das ist jetzt der Dereferenzierungsoperator, (was für ein Wort :D) der aus dem this Pointer wieder ein Objekt macht und das wird dann zurückgegeben.

Puh! ;)

Che
2003-07-19, 19:22:23
Hier hab ich wieder was:

Ich habe eine Exception Basisklasse
//-----Exception Basis-Klasse-----
//--------------------------------
class CBaseException
{
protected:
char Message[512];
int Line;
char *FileName;
unsigned int Type;

public:
//Constructors
//CBaseException();
CBaseException(const char *errormessage, unsigned int type, const char *file,
const int line);

unsigned int getType();

virtual void GenerateText();
virtual void Display();
};
und eine davon abgeleitete:
//-----OpenGL Exceptions-----
//---------------------------
class OGLException : public CBaseException
{
public:
OGLException(const char *errormessage, unsigned int type, const char *file,
const int line);

virtual void GenerateText();
virtual void Display();
};

Wenn ich das kompiliere regt er sich auf:

'CBaseException' : no appropriate default constructor available

Füge ich den ein (das rot markierte) gehts. Was ist da los?

Xmas
2003-07-19, 20:04:04
Original geschrieben von Che
Füge ich den ein (das rot markierte) gehts. Was ist da los?
Du benötigst hier anscheinend einen Default Constructor. Wenn du einen Constructor definierst, erzeugt der Compiler nicht mehr automatisch einen solchen Default Constructor. Deswegen musst du ihn selbst einfügen.

zeckensack
2003-07-20, 08:41:11
Der Konstruktor einer abgeleiteten Klasse ruft immer auch den Konstruktor der Basisklasse auf. Wenn du das genauer kontrollieren willst, dann mußt du das explizit machen, ie

OGLException::OGLException(int blah,int blub)
{
BaseException(blah,blub);
}

In deinem Fall würde es IMO reichen, den überladenen Konstruktor gleich ganz wegzulassen :)

Gast
2003-07-20, 09:47:29
Original geschrieben von zeckensack

In deinem Fall würde es IMO reichen, den überladenen Konstruktor gleich ganz wegzulassen :)

Wenn ich aber z.b bei einer datei-öffnen Exception weitere Infos übergeben will (Dateiname...) dann brauch ich schon einen eigenen Konstruktor. Was ich eigentlich wissen wollte, wieso braucht er den default Konstruktor überhaupt, wenn er doch nie zur Anwendung kommt?

Xmas
2003-07-20, 18:58:52
Original geschrieben von zeckensack
Der Konstruktor einer abgeleiteten Klasse ruft immer auch den Konstruktor der Basisklasse auf. Wenn du das genauer kontrollieren willst, dann mußt du das explizit machen, ie

OGLException::OGLException(int blah,int blub)
{
BaseException(blah,blub);
}

In deinem Fall würde es IMO reichen, den überladenen Konstruktor gleich ganz wegzulassen :)
Tut mir leid wenn ich dich da enttäuschen muss, aber das was du da geschrieben hast funktioniert nicht wie gewünscht. Der Konstruktor einer Basisklasse kann in C++ nur über die Initialisierungsliste aufgerufen werden.
Das einzige was die Anweisung oben tut ist ein temporäres Objekt zu erzeugen und gleich wieder wegzuwerfen.


OGLException::OGLException(int blah,int blub) : BaseException(blah,blub) {}

Wird die Basisklasse nicht auf diese Weise konstruiert, wird immer der Default Constructor dafür aufgerufen. Was wohl auch deine Frage beantworten dürfte, Che.

che g
2003-08-02, 12:19:34
Da blick ich noch nicht ganz durch: Der Konstrukor meiner Basisklasse nimmt z.B. 2 Argumente, der Konstruktor einer der abgleiteten Klassen aber 4. (Um den Fehler genauer zu spezifizieren)
Wenn nun aber IMMER der Basiskonstruktor aufgerufen wird funktioniert das doch gar nicht, denn der nimmt ja nur 2 Argumente.

Wird die Basisklasse nicht auf diese Weise konstruiert, wird immer der Default Constructor dafür aufgerufen. Was wohl auch deine Frage beantworten dürfte, Che.



btw. Wer hat sich da als Che angemeldet?
*g anhäng*

zeckensack
2003-08-02, 14:45:39
Danke @ XMas,
*peinlichsei*
:sulkoff:


Che,
Dann mußt du eben einen passenden Basiskonstruktor explizit aufrufen. Wie man das macht, hat XMas ja schon geschrieben (mein Codeschnipsel war Mumpitz) :)

zeckensack
2003-08-02, 14:49:52
Oder mal ganz ausführlich
class
Suppe
{
public:
Suppe(float viskosität):
viskosität(viskosität)
{in_den_teller_schütten(*this);}
private:
float viskosität;
};

class
PilzSuppe : public Suppe
{
public:
PilzSuppe(float viskosität,float pilzdichte):
Suppe(viskosität),
pilzdichte(pilzdichte)
{in_den_teller_schütten(*this);}
private:
float pilzdichte;
};

Mit der Präambel (die Initialisierungsliste zwischen Konstruktorkopf und -körper) kann man tolle Sachen machen =)

che g
2003-08-02, 16:24:55
Das muss ich mir wohl noch ein bisschen durchdenken, aber schön langsam kommt Licht ins Dunkel...


@Zecke: Hab' da was für deine Sig:
BOOL GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax)
[...]
Return Values
If the function retrieves a message other than WM_QUIT, the return value is nonzero.

If the function retrieves the WM_QUIT message, the return value is zero.

If there is an error, the return value is -1.

Und auf sowas baut das meistbenutzte Betriebssystem der Welt auf... :D