Überladen von << gibt Adresse von String statt String zurück...



  • Hallöle,

    ich bin gerade dabei, zu versuchen, wieder einmal ernsthaft in C++ einzusteigen. Ich habe durchaus umfangreiche Vorkenntnisse in C und Java, deshalb dachte ich eigentlich nicht, dass ich mit den Anfängen große Probleme bekomme, aber naja... man kann sich täuschen. 😞

    Ich habe eine HalloWelt-Klasse geschrieben (anfangs in getrennten Dateien, mittlerweile in einer, damit es Foren-freundlicher zum Posten ist). Den Quellcode seht ihr unten. Das Problem: Ich möchte den <<-Operator überladen! Leider gibt mir dieser allerdings nur die Adresse auf den String aus, nicht aber den Inhalt. Dabei wüsste ich nicht, was ich falsch gemacht habe! In Google finde ich keine brauchbaren Hinweise...

    Ich habe es auf g++ 4.4.3 Ubuntu und auf g++ 4.0.1 Apple, Inc probiert, beide haben den selben, eben beschriebenen Fehler.

    Gruß, Micha

    Quellcode:

    #ifndef MyProgramm
    #define MyProgramm
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    class HalloWelt {
    	public:
    		HalloWelt(const string t); 
    		friend std::ostream& operator<< (std::ostream&, HalloWelt&);
    	private:
    		std::string text;
    };
    
    HalloWelt::HalloWelt(const string t) {
    	text.assign(t);
    }
    
    std::ostream& operator<< (std::ostream& out,HalloWelt& hw) {
    	return out << hw.text;
    }
    
    int main (int argc, char** argv) {
    	HalloWelt* hallowelt = new HalloWelt("hi\n");
    	cout << hallowelt << endl;
    	return 0;
    }
    #endif
    


  • Da schlägt wohl die Java-Erfahrung durch 😉 In C++ gibt es einen Unterschied zwischen Zeigern (bzw. dem Speicher dahinter) und "normalen" Variablen. Also entweder du legst deine Variable auf den Stack oder du dereferenzierst den Zeiger, um an die Variable heranzukommen:

    //V1:
    HalloWelt hallowelt ("hi\n");
    cout << hallowelt << endl;
    
    //V2:
    HalloWelt* hallowelt = new HalloWelt("hi\n");
    cout << *hallowelt << endl;
    

    (PS: Und mach dich mal über const-Correctness und Initialisierungslisten schlau)

    Edit: Was mir noch aufgefallen ist: Include Guards im Hauptprogramm sind auch unnötig :D)



  • (PS: Und mach dich mal über const-Correctness und Initialisierungslisten schlau)

    Und darüber, dass zu jedem new ein delete gehört.



  • Oh man, was'n Fehler 😃 Ein Sternchen zu wenig und schon funktionier das Programm nicht mehr ^^ Danke für die schnelle Antwort 🙂

    CStoll schrieb:

    (PS: Und mach dich mal über const-Correctness und Initialisierungslisten schlau)

    Danke für die Stichwörter! Stimmt der untere Quellcode besser? Mit const-correctness bin ich immer noch ein bisschen unsicher, gibt es da einfache Richtlinien, an denen man sich orientieren kann? Sollte man einfach immer const setzen, solange man kein schlagfertiges Argument dagegen hat?

    Edit: Was mir noch aufgefallen ist: Include Guards im Hauptprogramm sind auch unnötig :D)

    Ja, ist ja auch gemein, wenn man Header und Cpp-Datei zusammenkopiert 😃

    Und darüber, dass zu jedem new ein delete gehört.

    Ja, du hast ja recht 😇 Das ist was ganz tolles an Java und Objective C 2.0 😉

    Quellcode (wegen const-Correctness)

    #include <iostream> 
    #include <string> 
    
    using namespace std; 
    class HalloWelt { 
        public: 
            HalloWelt(const string t) : text(t) {}; 
            friend std::ostream& operator<< ( std::ostream&, const HalloWelt&); 
        private: 
            std::string text; 
    };
    
    std::ostream& operator<< ( std::ostream& out, const HalloWelt& hw) { 
        return out << hw.text; 
    } 
    
    int main (int argc, char** argv) { 
        // Etwas übertrieben jetzt, aber im Prinzip doch genau das Richtige, für dieses Beispielprogramm?:
        HalloWelt const *const hallowelt = new HalloWelt("hi\n"); 
        cout << *hallowelt << endl;
        delete hallowelt;
        return 0; 
    }
    


  • Das new ist hier total unnötig.
    Erzeuge das HalloWelt Objekt doch einfach auf dem Stack:

    int main()
    {
       HalloWelt h("Hello World");
       cout << h << endl;
    }
    


  • Der Construktor sollte seinen Parameter per konstante Referenz übergeben (spart unnötige Kopien). Die übrigen Parameter sehen gut aus.
    Bei dem Zeiger bin ich mir jetzt nicht sicher, ob das ein wenig übertrieben ist - aber ich hätte das Objekt auch gar nicht erst per new angelegt, wenn es nicht unbedingt notwendig ist.



  • Wenn du wirklich C++ lernen willst, solltest du dir unbedingt ein gutes C++ Buch kaufen, in dem dir typische C++-Konzepte wir RAII nähergebracht werden.
    Wegen RAII braucht man nämlich new und delete außerhalb von Konstruktoren und Destruktoren sehr selten.

    RAII bedeutet so viel wie: Speicher per new im Konstruktor anfordern und per delete im Destruktor wieder freigeben. Und schon hast du deine Speicherverwaltung exceptionsicher gelöst.



  • Irgendwer schrieb:

    RAII bedeutet so viel wie: Speicher per new im Konstruktor anfordern und per delete im Destruktor wieder freigeben. Und schon hast du deine Speicherverwaltung exceptionsicher gelöst.

    Natürlich nur solange du auch die "Regel der Drei" beachtest (d.h. wenn du einen Destruktor benötigst, brauchst du auch Kopier-Construktor und Zuweisungsoperator).
    Übrigens geht es bei RAII nicht nur um Speicherverwaltung, sondern um alle Arten von Resourcen, bei denen du sicher sein mußt, daß sie ordentlich wieder freigegeben werden (Datei-Handles, Fenster-Handles, Grafik-Objekte etc).



  • CStoll schrieb:

    Der Construktor sollte seinen Parameter per konstante Referenz übergeben (spart unnötige Kopien).

    Stimmt. Hatte ich sogar mal drin in einer vorherigen Version.

    ich hätte das Objekt auch gar nicht erst per new angelegt, wenn es nicht unbedingt notwendig ist.

    Interessant, ich dachte immer, das sei der "normale" Weg in C++. Hatte ich noch von grafischen Oberflächen so in Erinnerung.

    Wenn du wirklich C++ lernen willst, solltest du dir unbedingt ein gutes C++ Buch kaufen, in dem dir typische C++-Konzepte wir RAII nähergebracht werden.

    Danke für den Tipp. Habt ihr irgendwelche Buchempfehlungen? Ich finde es schwer, etwas passendes zu finden: Bevor ich das Buch lese, kenne ich schließlich die typischen C++-Konzepte nicht, da lässt sich ein Buch schwer beurteilen. C++ hat viele Konzepte, die ich aus Java nicht kenne, da wäre es schön, ein Buch zu haben!
    Irgendwelche Tipps für ein Buch, dass nicht zu schwer zu lesen ist (englisch oder deutsch)?

    EDit: Genau so was meine ich: "Natürlich nur solange du auch die "Regel der Drei" beachtest" – Super, und sowas soll ich als Einsteiger wissen? Ich wäre wirklich dankbar für einen Buchtipp, der sich nicht (nur) an Anfänger richtet (also Grundlagen der OOP kann ich halt - wenn im Buch erst mal 30 Kapitel zu Vererbung stehen, finde ich das langweilig... es sei denn, es wird auf Unterschiede oder Sonderheiten von C++ eingegangen) 😉



  • Kümmelkorn schrieb:

    ich hätte das Objekt auch gar nicht erst per new angelegt, wenn es nicht unbedingt notwendig ist.

    Interessant, ich dachte immer, das sei der "normale" Weg in C++. Hatte ich noch von grafischen Oberflächen so in Erinnerung.

    Nein, ist in C++ überhaupt nicht üblich, im Gegensatz zu Sprachen mit Garbage Collector (z.B. Java oder C#).
    Als Einsteiger solltest du besser Objekte auf dem Stack unterbringen und existierende Klassen verwenden, die den ganzen RAII-Kram für dich übernehmen. Selber mit dieser Speicherverwaltung solltest du dich erst beschäftigen, wenn du dich ein wenig besser mit der Speicherverwaltung auskennst.



  • Für die Zukunft schon mal: Anstatt mit new ein Array anzulegen, nimm einen std::vector, der wächst sogar mit 😉



  • Kümmelkorn schrieb:

    Sollte man einfach immer const setzen, solange man kein schlagfertiges Argument dagegen hat?

    Richtig!

    Lg freeG



  • Setze const nur, wenn dein Code dadurch wirklich sicherer wird. Im folgenden Beispiel haben beide const keinen Vorteil, aber machen den Code unübersichtlicher:

    const MyClass Fn(const int i);
    

    Die häufigsten Anwendungsfälle sind:

    • const T& und const T* , also Referenzen und Zeiger auf konstante Objekte, vor allem in Parameterlisten
    • const T für funktionslokale Konstanten
    • void MyClass::MemFn() const für Memberfunktionen, welche das Objekt nicht ändern

Anmelden zum Antworten