Funktionen und Definition - Trennung in Headern



  • volkard schrieb:

    Nathan schrieb:

    Nie im Header!

    Meistens nie.
    Naja.
    Alle Generalisierungen sind falsch. Besonders die mit Rufzeichen!

    😃



  • Nathan schrieb:

    Steamoutputoperator

    Meinst du, dass ich mit
    Output << variable << "Text";
    das genauso füllen kann wie bei cout? Das ist da ja dann schon überladen.

    Vielen Dank für eure Antworten. 🙂

    Wenn ich jetzt zwei Klassen hätte und die andere Klasse B hätte Objekte von A (oder wäre eine Ableitung). Müssen diese Klassen in die selbe Datei oder in andere Headern?
    Ich hab probeweise die erste Klasse ausgelagert, den Rest in der main.cpp gelassen. Der Compiler sagt, er würde die Klasse A nicht kennen. 😕
    Liegt es vielleicht an der Reihenfolge, in der er die linkt? Der Header ist aber überall eingebunden, eigentlich müsste main.cpp sagen "brauche A, haben den Rest" und header "habe A, brauche nix" ... 😕

    Bei dem Minimalbeispiel, was hier steht, klappt das Problemlos.
    Ich bin irritiert .... 😕



  • Lymogry, wenn du Dir noch kein schlaues C++ Buch besorgt hast, wäre das jetzt eine prima Gelegenheit. Deine hier gestellten Fragen, lassen sich größtenteils beantworten, wenn man die Grundlagen (separate compilation, one definition rule) drauf hat.

    Lymogry schrieb:

    Wenn ich jetzt zwei Klassen hätte und die andere Klasse B hätte Objekte von A (oder wäre eine Ableitung). Müssen diese Klassen in die selbe Datei oder in andere Headern?

    Müssen tun die gar nichts. Halte Dich einfach an die one definition rule.

    Lymogry schrieb:

    Ich hab probeweise die erste Klasse ausgelagert, den Rest in der main.cpp gelassen. Der Compiler sagt, er würde die Klasse A nicht kennen. 😕
    Liegt es vielleicht an der Reihenfolge, in der er die linkt? Der Header ist aber überall eingebunden, eigentlich müsste main.cpp sagen "brauche A, haben den Rest" und header "habe A, brauche nix" ... 😕

    Wenn der Compiler sagt, dass er Klasse A nicht kenne, dann ist der Linker noch gar nicht zum Zug gekommen. Du wirst wohl irgendwo mindestens einen Fehler gemacht haben. Vielleicht hast du ja versehentlich 2mal denselben Include-Guard für zwei Header verwendet.



  • Ich hab gute Grundlagenbücher hier: Der C++ Programmierer, Primer, Stroustrup und Meyers.... (dann sieht man schonmal das Problem vor lauter Büchern nicht mehr)
    Und eigentlich kenne ich das mit dem Linken schon durch C und dachte, ich hätts schon verstanden, denn daa hatte ich nie Probleme mit dem Linker ...
    Ich dachte, dass mein Probleme nun durch die Klassen und deren Trennung daherrühren ... 😕

    Vielleicht hast du ja versehentlich 2mal denselben Include-Guard für zwei Header verwendet.

    hmm .. aber dafür habe ich ja den Präprozessor
    #ifndef HEADER1_H
    #define HEADER1_H

    muss ich da trotzdem aufpassen, dass ich die nicht den mehrfach nehme? Was ist mit den Stdbibliotheken?

    In C hats noch nie gemeckert .... (mache ich grad was anderes ... ich weiß es nicht ... 😞 )

    Du wirst wohl irgendwo mindestens einen Fehler gemacht haben.

    *lach* ja, davon geh ich auch aus ... 😃
    Ich breche das nun alles in ein Minimalbeispiel, aber so scheine ich alles richtig zu machen ... hmm hmm ...
    Ich breche noch runter 😃



  • ajajaj ... ich habs gefunden ... der Fehler ist eigentlich schon zu peinlich, um genannt zu werden ... 😃 oh man ...:
    Weil ich erstmal die Trennung der Klassen nicht hinbekommen habe (siehe ersten Post) ich hab das Programm mit Minimalbeispiel in Header geteilt. Die Minimalbeispiele liefen dann, also zurück ins Programm kopiert. Programm lief nicht.... Nun. Eine Klasse aus dem falschen Minimalbeispiel in .cpp kopiert, aber ihre richtige Form .hpp kopiert. Der Unterschied war winzig ... Und schon meinte er, er kenne die Klassen und die Funktionen nicht mehr ....

    sorry sorry .... 👎

    PS:

    (separate compilation, one definition rule

    ja, immer separat kompilieren lassen können! 🙂
    Keine doppelten Definitionen.
    Ist mir geläufig. Danke! 🙂

    Ihr seid spitze! 👍



  • ähm ... bei einer Sache konnte ich euch nicht ganz folgen:
    Wo soll der Output jetzt rein?
    So wie es da steht, erzeugt es ein Problem: 😕

    main.cpp

    #include <iostream>
    #include <fstream>
    #include "header1.hpp"
    
    std::ofstream Output("output.txt");
    
    int main() {
        return 0;
    }
    

    header1.hpp

    #ifndef HEADER1_HPP
    #define HEADER1_HPP
    
    class Ort {
    public:
        Ort(int einX=0, int einY=0);
        int getX() const;
        int getY() const;
        void verschieben(int x, int y);
        void anzeigen() const;
    private:
        int xKoordinate;
        int yKoordinate;
    };
    
    #endif // HEADER1_HPP
    

    header1.cpp

    #include <iostream>
    #include <fstream>
    #include "header1.hpp"
    
    Ort::Ort(int einX, int einY) : xKoordinate(einX), yKoordinate(einY) {}
    int Ort::getX() const {return xKoordinate;}
    int Ort::getY() const {return yKoordinate;}
    void Ort::verschieben(int x, int y) {xKoordinate=x; yKoordinate=y;}
    void Ort::anzeigen() const {
            std::cout << "(" << getX() << "," << getY() << ") ";
            Output << "Hallo";  // Fehler: "Output wurde in diesem Gueltigkeitsbereich nicht definiert!"
      }
    

    Merci 🙂



  • Natürlich kennt der Output nicht. Woher denn?
    Wenn der Compiler Header1.cpp kompiliert, weiß der nicht, was in main.cpp steht und umgekehrt.
    Lösung: Als Parameter übergeben oder deklarieren.



  • Nathan schrieb:

    Natürlich kennt der Output nicht. Woher denn?
    Wenn der Compiler Header1.cpp kompiliert, weiß der nicht, was in main.cpp steht und umgekehrt.

    schon klar.
    Ich dachte, globale Variablen sind jetzt auch klug und gehen mit 🙂 🙂

    Lösung: Als Parameter übergeben oder deklarieren.

    Das mit der deklaration versuche ich....
    Einmal
    std::ofstream Output;
    in header.hpp
    Und
    Output.open("output.txt")
    in main.

    Compiler sagt: Multiple Definition of Output ... 😕



  • header1.hpp

    #ifndef HEADER1_HPP
    #define HEADER1_HPP
    
    #include <ostream>
    
    class Ort {
    public:
        Ort(int einX=0, int einY=0);
        int getX() const;
        int getY() const;
        void verschieben(int x, int y);
        void anzeigen(std::ostream& wo) const; // <-- z.B. hier
    private:
        int xKoordinate;
        int yKoordinate;
    };
    
    #endif // HEADER1_HPP
    


  • Dann muss ich aber std::ostream& wo in jeder Subroutine mitschleppen, nur weil ich irgendwo mal eine Ausgabe haben möchte?

    Gibt es dazu eine Alternative? Kann ich irgendwie als Stdargument vielleicht "output.txt" hinschreiben?



  • Lymogry, globale Objekte sind sowieso (sehr oft) Käse.

    Offensichtlich kennst du das, was an "separate compilation" dranhängt doch nicht. Deswegen hab ich's ja auch erwähnt oben. Wenn der Compiler xxx.cpp kompiliert, guckt der sich höchstens die Dateien an, die dort per #include eingebunden werden. Das wars. Und bevor du etwas verwenden kannst, muss der Compiler vorher irgendwo eine Deklaration gesehen haben.

    Das, was du da im Header stehen hast, ist aber keine Deklaration. Es ist eine Definition. Du hast Output also 2mal definiert. Einmal in der Übersetzungseinheit main.cpp und einmal in header.cpp (wegen #include header.hpp). Aus der Definition kann man jetzt -- wenn unbedingt erwünscht -- eine Deklaration machen: Schreib extern davor.

    Das steht sicher alles in deinen schlauen Büchern. Mussu nur mal lesen. Das spart Dir und uns Zeit. :p (wirklich! auch dir!)



  • ok, Krümelkacker ..
    es war etwas krytisch, aber ich hab dich verstanden:

    Wenn ich den stream in main.cpp außerhalb von main() deklariere, innerhalb von main() öffne und in einer anderen Datei header.cpp mit extern nochmal deklariere, dann funktioniert es! 👍

    Ich weiß, dass globale Variablen kacke sind! Und dass deswegen der Linker nicht so recht funktioniert!
    Ich nutze keine einzige globale Variable, ausser diesem Filepointer!
    Weil:
    wenn es mehrer Funktionen sind, die wachsen, sich gegenseitig enthalten usw.
    Und irgendwann in einer dieser Subroutinen soll mal eine Dateiausgabe rein. Dann will ich das genauso unkompliziert haben, wie ein std:cout. Was soll ich also in JEDER Fkt meinen Filepointer mitschleppen?
    Dann hat eine globale Variable einen guten Sinn ....

    Oder hast du eine elegantere Option?
    Ich bin durchaus belehrbar und wissbegierig! 🤡



  • Wenn du Logging haben willst (so hab ich das verstanden), nimmste std::clog.
    In der main() machste dann:
    std::clog.rdbuf(output.rdbuf());
    Und siehe da: Alles wird in output hineingeschrieben.
    Für andere Ausgaben, ist die beste Lösung den Stream zu übergeben. Das ist zwar umständlich, aber glaub mir: Es ist besser.



  • Nathan schrieb:

    Wenn du Logging haben willst (so hab ich das verstanden), nimmste std::clog.
    In der main() machste dann:
    std::clog.rdbuf(output.rdbuf());
    Und siehe da: Alles wird in output hineingeschrieben.

    DANKE Nathen!
    Den Befehl kannte ich noch nicht. Das löst aber genau mein Problem!!!! MERCI! 🙂


Anmelden zum Antworten