Freie Funktionen in C++ und Einteilung der Module



  • Hallo!

    Frage 1:

    Ich ziehe allgemeine Funktionen eigentlich immer aus den Klassen raus, gerade wenn sie keinen Status brauchen.

    Also so in der Art:

    #ifndef MODUL_XY
    #define MODUL_XY
    
    class KlasseXY
    {
    public:
     void Methode1(int a); // ändert z.B. x
     void Methode2(int b); // braucht z.B. y als Status
    
    private:
     int x,y;
    };
    
    // diese Funktion gehört zwar insgesamt in den Bereich von MODUL_XY und KlasseXY, 
    // ist aber auf keinen Status von KlasseXY angewiesen - sprich - sie würde auch ganz ohne KlasseXY leben
    void FunktionXY(); 
    
    #endif
    

    Nun meinte heute mein Chef, was FunktionXY denn sei? Das ist ja überhaupt nicht objektorientiert und nicht gekapselt. "C Style".
    Ich meine, dass das den Vorteil hätte, dass ich
    1. allgemein drauf zugreifen kann
    2. keine Instanz von KlasseXY brauche, um die Funktion verwenden zu können

    Was ist eure Meinung dazu?
    Ich habe mich da eher an den Stil der STL gehalten, wo es Klassen gibt, aber genauso auch freie Funktionen.

    Frage 2:

    Und noch eine zweite Frage:
    Wenn ihr ein Modul habt mit Namen ModulXY.hpp, darf dort drinnen nur die Klasse ModulXY vorkommen, oder auch kleine Hilfsklassen, die dann aber anders heißen, z.B. MeineKleineHilfsklasseXY.
    Auch hier war mein Chef erstaunt, was denn in einer Datei ModulXY.hpp eine Klasse MeineKleineHilfsklasseXY zu suchen hat, die sollte man doch besser in eine eigene Datei geben.
    Ich mache mir gerne kleine Klassen, die Daten zusammenhalten und ein paar Methoden dazu definieren. Ich möchte aber gleichzeitig das Projekt nicht auf unzählige Dateien aufsplitten, nur weil ich mal eine neue Miniklasse einführe.

    Also so in der Art:

    #ifndef MODUL_XY
    #define MODUL_XY
    
    class Helper1
    {
    // ...
    };
    
    class Helper2
    {
    // ...
    };
    
    // Die Hauptklasse
    class KlasseXY
    {
    // ...
    };
    
    #endif
    

    Bitte um Eure Meinungen!



  • Bzgl. Frage 1: Im allgemeinen ist das okay. Die "objektorientierte" Alternative wäre eine statische Memberfunktion.

    Frage 2: Wie wäre es mit

    class KlasseXY {
    private:
       class helper { ... };
       class other_helper { ... };
    public:
       Rest...
    };
    

    Auf die Weise sind die Helper nach außen unzugreifbar und klar als Teil der Implementierung der eigentlichen Klasse gekennzeichnet.



  • @jajajajajajajajaja
    Ich mache das in beiden Fällen so wie du.
    Freie Funktionen in Klassen reinpacken, nur damit man keine freien Funktionen hat, macht für mich keinen Sinn.

    Bei Frage 2 grundsätzlich auch: ja, ist OK. Man sollte aber schon aufpassen dass man es nicht übertreibt. Weil sonst halt doch die Übersichtlichkeit leidet.

    Wobei das auch ein sehr subjektives Thema ist. Ich mag z.B. keine Projekte die auf 1000 Files bestehen wo überall nur 10-100 Zeilen drinstehen. Dann lieber 100 Files mit jeweils 100-1000 Zeilen. Und dementsprechend dann auch > 1 Klasse pro File. Aber wie gesagt: sehr subjektiv.



  • jajajajajajajajaja schrieb:

    Was ist eure Meinung dazu?
    Ich habe mich da eher an den Stil der STL gehalten, wo es Klassen gibt, aber genauso auch freie Funktionen.

    Es ist in C++ üblich und kein Problem freie Funktionen zu benutzen. Ich hab auch kein Problem damit und benutz auch öfter welche, nur bin ich mit sowas vorsichtiger als manch anderer hier im Forum und habe auch kein Problem damit, doch eine Klasse oder abstrakte Klasse zu definieren, auch wenn im Moment eine freie Funktion reichen würde. Vielleicht vor allem weil ich an einer sehr großen Software arbeite und bei uns so gut wie alles früher oder später an vielen Stellen mit verschiedenen Anforderungen benutzt wird.

    1. Wenn JETZT eine freie Funktion reicht, heißt es noch lange nicht, dass es auch in Zukunft so bleiben wird. Kann gut sein dass etwas später neue Anforderungen auftauchen und man plötzlich noch eine Funktion hat, die dazugehört und dann muss man schon 20 Stellen anpassen.
    2. Vielleicht braucht "die Funktion" aber doch einen Status. Jetzt vielleicht noch nicht, aber vielleicht bietet es sich bei mehr/anderen Daten an, Zwischenergebnisse zwischen Funktionsaufrufen zu cachen, damit ein zweiter Aufruf schneller geht. Geht mit einer Klasse transparent, mit einer freien Funktion nicht.
    3. Große und komplexe Funktionen sollte man natürlich grundsätzlich vermeiden, aber die gibts ja trotzdem. Und es kann sein, dass man die Funktion mal an einer anderen Stelle in leicht abgewandelter Form braucht, also das Verhalten irgendwie steuern will. Man könnte da 200 Parameter reingeben, oder vielleicht doch mal eine Klasse mit paar default Parametern, die man über Setter überschreiben kann.
    4. Grad bei Callbacks usw. ist es mir ab und zu lieber eine Schnittstelle zu definieren, statt std::function (bzw. bei mir boost::function, wir können noch kein C++11) zu verwenden. Ich verwende zwar viel häufiger boost::function und freie Funktionen, weil ich sowas einfach sehr oft benutze und kleine Hilfsfunktionen als Callbacks übergebe. Aber wenn das etwas größeres/wichtigeres ist, dann gebe ich auch mal explizit abstrakte Klassen als Schnittstellen an. Dann ist auch genau klar, was so ein Objekt macht und wie/wofür das benutzt wird und es ist nicht mehr nur irgendeine Funktion, die man irgendwo als Callback übergeben könnte oder auch nicht.

    Wenn dir aber eh schon klar ist, dass es nur kleine Hilfsfunktionen sind, dann spricht gar nichts gegen freie Funktionen. Kannst sie vielleicht noch in Namespaces stecken.



  • hustbaer schrieb:

    Ich mag z.B. keine Projekte die auf 1000 Files bestehen wo überall nur 10-100 Zeilen drinstehen. Dann lieber 100 Files mit jeweils 100-1000 Zeilen. Und dementsprechend dann auch > 1 Klasse pro File.

    Finde ich mittlerweile auch. Das liegt denke ich aber an der Syntax 😉 Ich habe früher vor allem C# entwickelt und da ist es selbstverständlich, dass jede Klasse in eine eigene Datei kommt. In C++ hab ich das zuerst auch so gemacht. Aber das hat mir nicht gefallen. Wenn ich die meisten interessanten Infos eh im Header habe und in der cpp Datei nur zwei Methodendefinitionen mit 20-50 Zeilen sind, dann kann ich mit so einer cpp wenig anfangen. Deswegen schreib ich jetzt auch eher Dateien mit 300-1000 Zeilen und jeweils mehreren internen Klassen.



  • Genau. Ich finde es auch praktisch dass man in den C++ Header Files schön eine Übersicht über einen grösseren Bereich haben kann.
    Also jetzt nicht übertrieben, eben ein paar zusammenhängende Klassen die man in vielleicht 1000-2000 Zeilen implementieren kann.

    Bzw. theoretisch kann man natürlich auch mit der 1:1 Symmetrie hpp:cpp brechen. Dann kann man auch Headers machen deren Implementierung in zig Files verstreut ist. Hab ich aber noch nie gemacht, und hat auch sicher wieder eigene Nachteile. z.B. dass Alt+O (Visual Assist 😵 nimmer funktioniert 😉



  • Freie Funktionen sind konvertierbar zu Funktionszeigern. Funktionszeiger sind Objekte und deren Typen sind Schnittstellen. Es sind sogar besonders schöne Objekte, denn man kann die Funktion über den Zeiger nicht verändern. Die Schnittstellen sind auch optimal, denn sie haben nur eine Methode, den Call Operator.

    "C-Style" mit freien Funktionen ist also objektorientierte Programmierung, die methodisch auch noch alles richtig macht (Immutability by default, minimale Schnittstellen).

    Dein Chef kann nicht programmieren. Das Vorhandensein des Schlüsselworts class ist unabhängig davon, ob das Programm objektorientiert ist.


Log in to reply