Operator-Overloadding und Streams Verständnisproblem
-
Hallo.
ich habe mir eine Klasse geschrieben die in eine Datei und gleichzeitig auf std::cout loggen kann. Da ich mich mit Streams noch nie wirklich intensiv beschäftigt habe, habe ich meine Schwierigkeiten etwas zu verstehen.
Hier der komplette Code:
class logger { public: logger() { ofs_.open("log.log", std::ios::app); } ~logger() { ofs_.close(); } template<typename T> logger& operator <<(const T &data) { ofs_ << data; std::cout << data; return *this; } typedef std::basic_ostream<char, std::char_traits<char> > cout_type; typedef cout_type& (*default_endline)(cout_type&); logger& operator <<(default_endline manip) { manip(ofs_); manip(std::cout); return *this; } private: std::ofstream ofs_; };Mein Problem beginnt ab Zeile 22.
typedef std::basic_ostream<char, std::char_traits<char> > cout_type; typedef cout_type& (*default_endline)(cout_type&); logger& operator <<(default_endline manip) { manip(ofs_); manip(std::cout); return *this; }Der erste Typedef ist klar ich erstelle mir einen Alias für den type std::basic_ostream<char, std::char_traits<char> > was ja auch std::cout und std::endl; entspricht. Soweit so gut.
Aber was ist das hier: *typedef cout_type& (default_endline)(cout_type&);Nur dadurch ist so was möglich:
logger l; l << "test " << 123 << std::endl;Es wird also nur für std::endl benötigt. Was ich doch sehr verwirrend finde, da ich ja schon eine Template Funktion habe die jeden Datentyp entgegen nehmen kann, also warum auch nicht diesen.
Qt kann das (glaube ich), das habe ich mal in einem Beispiel gesehen.
#include <qdebug> qDebug() << "Hallo Welt!" << std::endl;Ich will das auch haben
Die schreibweise ist super.Über ein paar Tipps wäre ich dankbar.
Grüße.
-
// Edit
Sorry habe einige zeilen Text vergessen, den letzten Teil recht unvertändlich macht. Ich kann leider meinen Beitrag nicht mehr ändern.Dann hätte ich noch eine andere Frage zur Verwendung.
Ich würde meine Klasse gerne so verwenden, wie man es mit std::cout auch macht, ohne manuell ein Objekt der Klasse zu erzeugen. Kann mir jemand sagen wie man das Realisiert? Macros wären auch ok.Es wird also nur für std::endl benötigt. Was ich doch sehr verwirrend finde, da ich ja schon eine Template Funktion habe die jeden Datentyp entgegen nehmen kann, also warum auch nicht diesen.
Qt kann das (glaube ich), das habe ich mal in einem Beispiel gesehen.
#include <qdebug> qDebug() << "Hallo Welt!" << std::endl;Ich will das auch haben
Die schreibweise ist super.Sorry für das kleine Durcheinander, werde mir bei Gelegenheit einen Account anlegen.
-
create_problem schrieb:
Aber was ist das hier: *typedef cout_type& (default_endline)(cout_type&);
Allgemein:
default_endlineist ein Zeiger auf eine Funktion, die einen Parametercout_type&hat undcout_type&zurückgibt.
Praktisch: Da das Ganze mittypedefgemacht wird, istdefault_endlinenun ein Typ für solch einen Zeiger. Dann kannst du das machen:cout_type& fkt(cout_type& ct) { } default_endline p = &fkt; // Der Zeiger p zeigt auf die Funktion fkt.Offiziell schaut das eigentlich so aus:
typedef std::basic_ostream<char, std::char_traits<char> > ostream;Du hast nun inoffiziell das gemacht:
typedef std::basic_ostream<char, std::char_traits<char> > cout_type;Fazit:
cout_typeistostream.create_problem schrieb:
Es wird also nur für std::endl benötigt.
Ja. Ich hab mal anschaulicher gemacht:
class logger { public: logger& operator <<(default_endline manip) { manip(ofs_); manip(std::cout); return *this; } }; std::ostream& neue_zeile(std::ostream& os) { os << '\n'; return os; } int main() { logger l; l << neue_zeile; }l << neue_zeileist dasselbe wiel.operator<<(neue_zeile).neue_zeileist eine Funktion, die einostreamArgument erwartet und einostreamzurückgibt. Wir erinnern uns, dass deincout_typejaostreamist.
3. Deinoperator<<(default_endline manip)will als Argument eine Funktion, die einostreamArgument erwartet und einostreamzurückgibt.neue_zeileerfüllt diese Anforderungen.manipist dann ein Zeiger auf die Funktionneue_zeile.manip(std::cout)ruft dann schlußendlich die Funktionneue_zeileauf.
Achja, und der Grund, wieso nicht
template<typename T> logger& operator <<(const T &data)sondern
logger& operator <<(default_endline manip)genommen wird, ist einfach, weil letzteres spezieller ist, es ist einfach ein besserer match.
Ich hoffe, das du es nun verstanden hast.

-
Hallo.
Danke für deine Antwort, war sehr aufschlussreich. Ich habe es nun verstanden und auch festgestellt das ich auf den Gebiet Nachholbedarf habe.
Grüße.