Ansammlung von Funktionen in header Datei?



  • Hallo,

    wie macht ihr das eigentlich immer?

    ich will viele Funktionen in einer Datei schieben so dass ich nur noch #include hinschreiben muss, damit die Funktionen in main und in anderen Klassen alle funktionieren. sowas wie math.c und andere module

    zuerst hab ich einfach ein paar Funktionen in header-datei geschrieben, dann in main-datei #include davon gemacht somit kann man in dieser datei diese funktionen abrufen - es funktioniert. sobald ich in irgendwelchen Klassen in anderen header-Dateien dasselbe includiere, kollidiert es.

    ich hab die Funktion dann mit dem schlüsselwort static deklariert und compilerfehler verschwindet.

    aber dann bekomme ich andere seltsame Probleme, dass diese Funktionen in klassendateien scheinbar keine Ergebnisse zurückliefern. (pointer bei return zeigt immer noch auf null) nur in main funktioniert

    wie macht ihr das normalerweise?



  • zenturio13 schrieb:

    Hallo,

    wie macht ihr das eigentlich immer?

    ich will viele Funktionen in einer Datei schieben so dass ich nur noch #include hinschreiben muss, damit die Funktionen in main und in anderen Klassen alle funktionieren. sowas wie math.c und andere module...

    1. Grundsätzlich vermeide ich zu große Headerdateien. Es mag zwar bequem sein nur einen Header zu inkludieren, aber eine logische Trennung macht es lesbarer.
    2. Strikte Trennung von Deklaration (.h) und Definition (.cpp) - Ausnahmen sind ausschließlich Templates
    3. Includeguards berücksichtigen
    4. Möglichst wenige Abhängigkeiten in Headern (ggf. Vorwärtsdeklarationen)
    ...

    cu André



  • includeguards habe ich gemacht

    das sind diese #ifndefine _BLAH_ oder?

    ja wie soll das aussehen? ich schreibe nur funktionen ohne klassen

    in header:
    void fkn(int irgendwas);

    in cpp

    void fkn(int irgenwas) {...}

    so? ich bemerkte dass ich static wieder weglassen kann. komisch?

    nur die funktion liefert immer noch kein ergebnis zurück wenn ich sie in der klasse ausführe. nur wenn ich den gesamten code der funktion in die klasse kopiere, gehts wieder. das macht mit der ansammlung von funktionen aber keinen sinn !



  • achso in diesem beispiel habe ich void angegeben. natürlich sind die funktionen nicht mit void sondern mit

    SDL_Surface *load_image(std::string filename)
    {...
    return optimizedImage
    }
    


  • das sind diese #ifndefine _BLAH_ oder?

    ja wie soll das aussehen? ich schreibe nur funktionen ohne klassen

    Eigtl. sind Namen mit _ dem Compiler zugeordnet. Von d.h. sollte man sie nicht nutzen.

    #if !defined(FOO_HPP__INCLUDED)
    #define FOO_HPP__INCLUDED
    
    // ...
    
    #endif // FOO_HPP__INCLUDED
    

    --- für Microsoft Compiler kann man noch #pragma once nehmen. Hat seine Vorteile ... (such mal im Forum danach).

    #if (_MSC_VER >= 1300)
    #pragma once
    #endif // (_MSC_VER >= 1300)
    

    ...

    Zu deiner Funktion:

    SDL_Surface* load_image(std::string const& file_name)
    {...
        return optimizedImage
    }
    

    nimm konstante Referenzen bei der Parameterübergabe. Dann wird am Ausgangsobjekt nix verändert und du kopierst nicht erst das Objekt, bevor du es nutzt.



  • zenturio13 schrieb:

    includeguards habe ich gemacht

    das sind diese #ifndefine _BLAH_ oder?

    ja wie soll das aussehen? ich schreibe nur funktionen ohne klassen...

    Ich mache dir aber mal ein kurzes Beispiel für eine Trennung:

    funk.h

    #if !defined(FUNK_HEADER)
    #define FUNK_HEADER
    
    namespace MyProject
    {
        int add(int a, int b);
        int sub(int a, int b);
    }
    
    #endif
    

    funk.cpp

    #include "funk.h"
    
    namespace MyProject
    {
        int add(int a, int b)
        {
            return a+b;
        }
    
        int sub(int a, int b)
        {
            return a-b;
        }
    }
    

    main.cpp

    #include <iostream>
    #include "funk.h"
    
    int main()
    {
        std::cout << "2+4=" << MyProject::add(2, 4) << std::endl;
        std::cout << "2-4=" << MyProject::sub(2, 4) << std::endl;
    }
    

    Dieses Beispiel zeigt dir auch das die Funktionen Werte zurückliefern. Wunder dich nicht über den Namespace, ich verwende eigentlich grundsätzlich Namensräume (und die Schreibweise #if !defined(...) ziehe ich #ifndef ... vor, da sie IMHO aussagekräftiger ist und eher C++ entspricht).

    zenturio13 schrieb:

    so? ich bemerkte dass ich static wieder weglassen kann. komisch?

    Wieso komisch? Wenn ich Funktionen ausschließlich im Header implementiere, so weist mich mein Compiler auch über Codedublikate hin, sofern ich den Header an mehreren Stellen includiere.

    zenturio13 schrieb:

    nur die funktion liefert immer noch kein ergebnis zurück wenn ich sie in der klasse ausführe. nur wenn ich den gesamten code der funktion in die klasse kopiere, gehts wieder. das macht mit der ansammlung von funktionen aber keinen sinn !

    Also deine Fehlerbeschreibung hat meines Erachtens nichts mit der Trennung in Funktionen zu tun, da liegt eher ein Fehler in den Funktionen oder Klassen selber vor. Wenn du mein Minibeispiel ausprobierst wirst du auch sehen das es Ergebnisse liefert.

    Aber davon mal abgesehen:

    zenturio13 schrieb:

    SDL_Surface *load_image(std::string filename)

    So eine Funktion entfinde ich schon problematisch:

    1. Wie ist die Speicherstrategie? Mit new, mit free, oder ist es gar ein Zeiger auf eine statische Variable? Anhand der Funktion sieht man ihr das nicht an.
    2. Objekte sollte man nie "by value" übergeben, lieber als konstante Referenz.

    Mögliche Alternative (mit boost):

    #include <string>
    #include <boost/shared_ptr.hpp>
    
    boost::shared_ptr<SDL_Surface> load_image(const std::string& filename) ...
    

    cu André



  • asc schrieb:

    2. Strikte Trennung von Deklaration (.h) und Definition (.cpp) - Ausnahmen sind ausschließlich Templates

    ausnahmen sind *klassen*. du meintest wohl die trennung zur implementation.
    eine klassendefinition ist bereits ohne implementation eine definition, d.h. zweimal in der selben übersetzungseinheit definieren, verstößt gegen die ODR. klassendeklarationen (forward declaration) und funktionsdeklarationen kann man so beliebig oft wie gewünscht einführen, dafür sind include guards gar nicht nötig. (in deinem (asc) letzten codebeispiel ist das zum beispiel so) - heißt natürlich nicht, dass man auf include-guards verzichten soll.

    @OP: static in diesem kontext ersetzt man durch "unnamed namespaces", um die sichtbarkeit der variable/funktion auf die aktuelle übersetzungseinheit zu beschränken. nicht, dass es dann funktioniert; aber du solltest dir ergooglen, was static hier bedeutet.



  • queer_boy schrieb:

    asc schrieb:

    2. Strikte Trennung von Deklaration (.h) und Definition (.cpp) - Ausnahmen sind ausschließlich Templates

    ausnahmen sind *klassen*. du meintest wohl die trennung zur implementation.

    Nein, ich meine was ich sage: Deklaration (dazu gehören auch Klassendeklarationen) und Definition )Implementierung). Mag sein das ich 10 Jahre lang falsche C++ Bezeichnungen verwende (und diese auch so gelernt habe).

    cu André



  • Hab grad den Thread hier gefunden, und mich wundert hier, dass folgendes funktionieren soll:

    faq.h

    int addiere(int a,int b)
    {
       return (a+b);   
    }
    

    limp.cpp

    #include <iostream>
    #include "faq.h"
    using namespace std;
    int main(void)
    {
       addiere(4,5);
       system("Pause");
    }
    

    Er findet nämilch die Funktion 'addiere' nicht. (Addiere ist ja auch schon definition)

    Zudem wundert mich, dass Klassendefinition nicht in Headerdateien gehört.
    Weil eine Deklaration wäre ja:

    class hallo;
    

    und eine Definition schon:

    class hallo
    {
       private:
          int a;´
    };
    

    Und die Definition steht bei mir immer in den Headern- das haben wir sogar in der Schule gelernt.



  • 777 schrieb:

    Hab grad den Thread hier gefunden, und mich wundert hier, dass folgendes funktionieren soll:

    faq.h

    int addiere(int a,int b)
    {
       return (a+b);   
    }
    

    limp.cpp

    #include <iostream>
    #include "faq.h"
    using namespace std;
    int main(void)
    {
       addiere(4,5);
       system("Pause");
    }
    

    Er findet nämilch die Funktion 'addiere' nicht. (Addiere ist ja auch schon definition)

    Wieso sollte er die Funktion nicht finden? Eine Definition ist auch immer eine Deklaration (umgekehrt nicht). Der Linker könnte höchstens Probleme bekommen, wenn er mehrere Dateien zusammenfassen soll, die alle die "faq.h" eingebunden haben - dann hat er plötzlich mehrere Definitionen, unter denen er auswählen kann.

    Zudem wundert mich, dass Klassendefinition nicht in Headerdateien gehört.
    Weil eine Deklaration wäre ja:

    class hallo;
    

    und eine Definition schon:

    class hallo
    {
       private:
          int a;
    };
    

    Und die Definition steht bei mir immer in den Headern- das haben wir sogar in der Schule gelernt.

    Das sind beides Deklarationen (das erste eine Forward-Deklaration der Klasse, das zweite deklariert deren Member - die Definition geschieht später (statische Member und Methoden mußt du nochmal explizit angeben, normale Member werden implizit definiert, wenn du ein Objekt dieser Klasse anlegst).



  • 777 schrieb:

    Hab grad den Thread hier gefunden, und mich wundert hier, dass folgendes funktionieren soll:

    faq.h

    int addiere(int a,int b)
    {
       return (a+b);   
    }
    

    limp.cpp

    #include <iostream>
    #include "faq.h"
    using namespace std;
    int main(void)
    {
       addiere(4,5);
       system("Pause");
    }
    

    warum sollte das nicht funktionieren? warum sollte er die definition nicht finden? nach dem durchgang des präprozessors sieht deine datei so aus:

    /* was immer in iostream steht */
    int addiere(int a,int b)
    {
       return (a+b);   
    }
    
    using namespace std;
    int main(void)
    {
       addiere(4,5);
       system("Pause");
    }
    


  • CStoll schrieb:

    Zudem wundert mich, dass Klassendefinition nicht in Headerdateien gehört.
    Weil eine Deklaration wäre ja:

    class hallo;
    

    und eine Definition schon:

    class hallo
    {
       private:
          int a;
    };
    

    Und die Definition steht bei mir immer in den Headern- das haben wir sogar in der Schule gelernt.

    Das sind beides Deklarationen (das erste eine Forward-Deklaration der Klasse, das zweite deklariert deren Member - die Definition geschieht später (statische Member und Methoden mußt du nochmal explizit angeben, normale Member werden implizit definiert, wenn du ein Objekt dieser Klasse anlegst).

    das ist eine definition. und auch für die member der klassen kann man das definition nennen (wie es der standard auch tut, und nicht bloß deklaration). nur statische member werden in einer klassendefinition bloß deklariert und müssen danach (nicht nochmal, sondern zum ersten mal) definiert werden. dein erstes beispiel class X; muss noch nichtmal eine forward-deklaration sein, es kann auch einfach eine redeklaration einer bereits definierten klasse sein.

    regel: eine deklaration _ist_ eine definition, außer (unter anderem) sie deklariert eine statische elementvariable (siehe v.a.: 3.1/2 und 3, 9.1., zu statischen membern: 9.4.2/2)



  • irgendwie hat mich das jetzt etwas durcheinander gebracht^^

    http://www.c-plusplus.net/forum/viewtopic-var-t-is-61231.html

    Dort wird ganz klar gesagt, dass Deklarieren nur heisst einen bestimmten Namen in einem Scope zu reservieren.

    extern int a;
    
    void foo(int);
    
    class hallo;
    

    Oder etwas in der Richtung.

    Definieren heisst, dass der Compiler speicher belegt.
    Also welche der Aussagen stimmt jetzt?^^

    class hallo 
    { 
       private: 
          int a; 
    };
    

    Belegt ja in dem Sinne auch noch keinen Speicher sondern gibt nur die Namen der Member der Klasse bekannt.
    Also kann letztriger Codeschnipsel doch in die Header <-- Ist also eine klare DEKLARATION?
    Doch was käme dann von der Klasse 'hallo' noch in die ausgelagerte Cpp?



  • Ja, das kann schonmal zu Verwirrungen führen. Die Klassendefinition gehört in den Header (aus technischer Sicht würde ich das auch als Deklaration ansehen), in die CPP gehört die Definition der statischen Member und Methoden (die bei dieser Klasse nicht existieren):

    //Klassen-Definition - gehört in den Header
    class test
    {
      int i;
      static int s;
    public:
      void print();
    };
    
    //Member-Definitionen - gehören in die CPP
    int test::s;
    
    void test::print()
    {
      cout<<s<<" "<<i<<endl;
    }
    
    //test::i muß nicht gesondert definiert werden - das lebt innerhalb der 'test'-Objekte, die du irgendwo im Programm anlegen kannst
    


  • Das wiederum würde aber auch bedeuten, dass alle Elementfunktionen ausgelagert werden müssen.
    Also sollte KEINE davon innerhalb der Klasse definiert werden, wie man es auch häufig im Netz sieht.
    Sondern immer mit dem Zugehörigkeitsoperator in eine andere Datei - die Cpp-Datei?

    Also wäre die Regel auch:

    Keine Elementfunktionen innerhalb der Klasse definieren.

    ?^^



  • 777 schrieb:

    Also wäre die Regel auch:

    Keine Elementfunktionen innerhalb der Klasse definieren.

    ?^^

    Nicht ganz - inline Funktionen dürfen (und müssen) im Header definiert werden (weil der Compiler den Quelltext benötigt, um sie inline ersetzen zu können). Und Methoden, die direkt in der Klassendefinition definiert werden, gelten automatisch als inline.



  • Also dürfen in den Header nur Deklarationen, mit großer Ausnahme von Inlinefunktionen? 😉
    Okay... super- ich denk ich habs verstanden xD



  • 777 schrieb:

    Definieren heisst, dass der Compiler speicher belegt.

    vielleicht könnte man diesen punkt genauer herausarbeiten als: definieren heißt, das an der stelle der definition dem compiler bekannt gemacht wird, wieviel speicher er für eine gewisse entität bereit stellen soll - was nicht heißt, dass er das auch machen muss. ein schönes beispiel dafür sind klassendefinitionen, ein anderes compiletime-konstanten, für die auch nur bei bedarf speicher reserviert wird. (wobei man sich da natürlich auf dünnem eis bewegt, der standard definiert definition als eine deklaration, bei der gewisse bedingungen nicht gegeben sind)

    und um dich nochmal zu verwirren, du kannst in einem header auch "normale" definitionen haben, ohne je probleme beim kompilieren oder linken zu bekommen, z.b.

    namespace { int i; }
    const int x = 42;
    


  • Was man kann und was man soll ist ja nochmal ein Unterschied.
    Es geht mir ja darum zu lernen, was man sollte.
    Und wenns besserer oder übersichtlicherer Programmierstil ist in die Header nur Deklarationen zu schreiben- so mach ich das 😉



  • damit wollte ich nur sagen, dass es sehr wohl auch ausnahmen geben kann. die integralkonstante ist ein gutes beispiel dafür. in den header oder nicht? nein? was ist mit enums?

    wichtig ist, die ODR zu kennen und zu wissen, was übersetzungseinheiten sind - und dass der präprozessor nur eine einfache textersetzungsmaschine ist.


Anmelden zum Antworten