Klassen: privat Funktion/Variable "verstecken"



  • Ich bin jetzt schon den ganzen Tag am Suchen, mein Problem beim Suchen ist dabei jedoch oftmals, man erfährt meist nur wie man etwas mach soll, und solange man "brav" alles genau so macht, sollte auch alles gut funktionieren. Es ist jedoch schwierig herauszufinden, was genau man tatsächlich machen muß, bzw. herauszufinden ob etwas auch fehlerfrei funktionieren würde, wenn ich es anders machen will als es üblich wäre, einschließlich was ich dann dabei beachten müßte.
    Wenigsten lernt man dabei trotzdem so das eine oder andere "Neue" dazu.

    Zu meiner Frage:
    Im Aktuellen Fall befasse ich mich gerade mit Klassen.
    Was muß unbedingt in der Header-Datei stehn.
    Was davon könnte ich auch in die cpp-Datei schreiben.
    Wie müßte es dort stehen und welche Konsequenzen hätte dies.

    Etwas Konkreter:
    Frage1 ( Verständnisfrage ): "public" wird zeitweise etwas widersprüchlich beschrieben. Manchmal steht da dann einfach nur, lapidar beschrieben, alle könnten auf "puplic" zugreifen.
    Wenn ich das ganze jedoch richtig verstanden habe kann nur ein Objekt der Klasse (bzw. Ein Objekt dessen Klasse diese Objekt enthält, also ObjektKlasse2.ObjektKlasse0) darauf zugreifen.
    Ist meine Interpretation richtig?

    Frage2: Könnte ich zusätzlich zu privat auch noch ein "hiden" machen. Sprich wenn irgend wo steht "#include Klassenname.h" dann wird ja später diese Stelle ersetzt mit allem was in "Klassenname.h" steht, enschließlich dem was unter "privat" aufgelistet ist. Könnte ich dies eventuell auch umgehen in dem ich den "hiden"-Teile von "privat" direkt in die cpp-Datei schreibe.
    Beispiel:

    Klasse.h:

    class Klasse {
    public:
          int publicWert = 0;
          int PublicFunktion();
    
    private:
          int privateWert = 0;
          int PrivateFunktion();
    };
    

    Klasse.cpp:

    #include Klasse.h
    
    int hidenWert = 0;
    int HidenFunktion();
    
    int Klasse::PublicFunktion() {return PrivateFunktion();}
    int Klasse::PrivateFunktion() {return HidenFunktion();}
    
    int Klasse::HidenFunktion() {
          int hidenFunktionsWert = 0;
          publicWert = 1;
          privateWert = 2;
          hidenWert = 3;
    
          return hidenFunktionsWert;
          }
    
    • Was davon würde so wie es jetzt ist (Tippfehler mal ausgenomen) auch Funktionieren?
    • Was davon würde Funktionieren wenn ich entsprechende Änderungen vornehme?
    • Bräuchte ich das "Klasse::" vor "hidenFunktion()", falls das mit der "hidenFunktion()" überhaupt funktioniert?

    Frage3 : Würde folgendes Funktionieren:
    Beispiel:

    Klasse.h:

    class Klasse {};
    

    Klasse.cpp:

    #include Klasse.h
    
    class Klasse {
    public:
          int publicWert = 0;
          int PublicFunktion();
    
    private:
          int privateWert = 0;
          int PrivateFunktion();
    };
    
    int Klasse::PublicFunktion() {return PrivateFunktion();}
    int Klasse::PrivateFunktion() {return privateWert;}
    

    Falls dies funktionieren würde, bräuchte ich in "Klasse.cpp" dann überhaupt noch ein "#include Klasse.h"?



  • @axam sagte in Klassen: privat Funktion/Variable "verstecken":

    Frage1 ( Verständnisfrage ): "public" wird zeitweise etwas widersprüchlich beschrieben. Manchmal steht da dann einfach nur, lapidar beschrieben, alle könnten auf "puplic" zugreifen.
    Wenn ich das ganze jedoch richtig verstanden habe kann nur ein Objekt der Klasse (bzw. Ein Objekt dessen Klasse diese Objekt enthält, also ObjektKlasse2.ObjektKlasse0) darauf zugreifen.
    Ist meine Interpretation richtig?

    Nope. access specifiers

    Was Du mit deinen

    int hidenWert = 0;
    int HidenFunktion();
    

    machst hat nichts mit der Klasse zu tun. Das eine ist eine globale Variable und das andere eine freie Funktion. Die kann jeder nutzen der ihre Deklaration kennt.



  • @Swordfish also wenn ich deinen englischen Text, zu dem dein Link führt, richtig verstehe, dann gibt es keine Möglichkeit den Zugriff auf eine Variable/Funktion auf Objekte zu begrenzen. Ist etwas public genügt Klassenname:: und in allen anderen Fällen hat auch ein Objekt der Klasse keinen Zugriff.

    Wenn ich deinen 2 Hinweis richtig verstehe bedeutet dies, ich könnte in die cpp-Datei die zur Klasse gehört ohne weiteres eine frei Funktion schreiben, und nur Funktionen die in dieser cpp-Dateien stehen, kennen ihre Deklaration, weshalb auch nur diese darauf Zugriff haben. Im Grunde also genau was ich wollte.
    Zudem ist durch deinen Hinweis auch mein Verständnis von Klassen besser geworden, den wenn ich dich richtig verstehe, bedeutet dies, eine Klasse ist nicht das was in Header und cpp steht, sondern das was zischen {}; steht bzw. deklariert wird.

    Bleib noch die Frage, ob ich Teile dessen was zwischen {}; steht auch irgend wie in die .cpp schreiben kann.
    Keine Ahnung ob dies Funktionieren würde, ich könnte mir jedoch vorstellen für jede Klasse in der .cpp eine eigene PrivateKlasse zu schreiben in der dann alles publik ist. Mit "PrivateKlasse::" hätte dann zwar "jeder" in der .cpp-Datei der Klasse, Zugriff darauf jedoch nichts ausserhalb der .cpp-Datei, und somit auch nicht ausserhalb der Klasse (möglicherweise würde es auch irgend wie mit prodected funktionieren ). Stellt sich jedoch die Frage, ob dies dann nicht erst wieder wie mit freien Funktion wäre, nur eben das selbe in grün.
    Oder würde das so, wohl ohnehin nicht funktionieren?
    Andere Möglichkeiten / bessere Vorschläge um zum einen die Header-Datei möglichst klein zu halten, und zusätzlich auch gleich noch Variable/Funktionen dem Zugriff von "außen" zu entziehen (bin mir dabei jetzt nicht ganz sicher, was von beidem mir dabei am Ende wirklich wichtiger ist )?



  • @axam
    Warum probierst Du nicht selbst aus, was geht und was nicht?

    Beispiel (alles in einer *cpp-Datei):

    class test
    {
       public:
          int test_public;
    
      private:
          int test_private;
    };
    
    int main()
    {
       test t;
       t.test_public = 5; // geht, weil public - Variable
       t.test_private = 6; // geht nicht, weil private
    }
    


  • Dafür gibt es das PIMPL-Idiom ("Pointer to implementation") - Beispiel: PImpl Idiom in C++ with Examples.



  • @all Brauche Bestätigung. Würde folgendes Funktionieren?
    Im Schlaf liegt Segen. Über Nacht kam mir vermutlich eine "regelkonforme" Lösung.

    Also, an anderer Stelle hier im Forum habe wurde mir ja bereits bestätigt, daß ich auch mehr als eine Headerdatei haben kann.
    Welche Konsequenzen hätte es, wenn ich für jede Klasse 5 Headerdatein schreibe, oder wäre das eine "gute" Lösung.

    • In der >KlassenHeader.h< steht dann Regelkonform die vollständige Klasse, alles von Public bis "hiden". Dieser >KlassenHeader.h< wäre dann exklusiv für die >Klassen.cpp< und wird nirgendwo anders im Programm genutzt.

    • Die >Klassen.h< hingegen darf mit #include in jede .cpp eingebunden werden, enthält jedoch nur den Public-Teil der Klasse.

    • Die >KlassenZeiger.h< ist "leer":

    class Klasse {};
    //Wird verwendet wenn eine .cpp nur Zeiger auf die Klasse benötigt.
    
    • Analog dazu enthält >KlassenProtected.h< neben dem public Teil auch alles was als protected eingestuft wurde.

    • In >KlassenPrivate.h< fehlt dann nur noch was exklusiv intern für die Klasse gedacht ist und ich darum "hiden" haben möchte.

    @Belli
    Dies mag jetzt vielleicht für manchen wie eine faulen Ausrede klingen, aber anders als bei euch, erscheint mir der Compiler kein zuverlässige Quelle. Wenn hier im Forum jemand etwas sagt, dann ist das auch so, den würde er sich irren, käme sofort ein anderer, der ihn richtig stellt.
    Die Reaktion des Compiler kann jedoch viele Gründe haben, erstmal muß ich unterscheiden ob eine Fehlermeldung bedeutet "geht nicht" oder "tippfehler", wenn es kein "tippfehler" ist, sagt er nur "so geht es nicht", er wird mir niemals vorschlagen, was ich anders machen könnte, er wird mir auch niemals sagen, so funktioniert es, aber dies und das wäre eine besser Möglichkeit und zu guter letzt bedeute das fehlen einer Fehlermeldung keines wegs das ich alles richtig gemacht habe, sondern nur das es dem Compiler jetzt egal ist, kann aber trotzdem noch dazu führen, daß das Programm etwas anderes tut als das was ich tatsächlich will.

    Nachtrag: Während ich obiges geschrieben habe, wurde mir "PImpl Idiom" empfohlen. Klingt auf den ersten Blick durchaus vielversprechend, muß ich mir jedoch erst noch im Detail genauer ansehen.



  • @axam sagte in Klassen: privat Funktion/Variable "verstecken":

    Welche Konsequenzen hätte es,

    Es wäre falsch. Stichwort One Definition Rule



  • Und was @manni66 mit "falsch" meint ist: es ist explizit vom C++ Standard verboten, Stichwort "one definition rule".
    (Der Term "falsch" war da schon korrekt verwendet, ist also keine Korrektur. Ich wollte nur klarstellen dass er nicht frei im Sinn von "wir finden das furchtbar und nennen es deswegen falsch" verwendet wurde.)

    (EDIT: War der Link zur "one definition rule" immer schon in @manni66 s Beitrag? Wenn ja hab ich wohl nach "Es ist falsch" aufgehört zu lesen und mein Hirn ausgeschaltet. Hmmm...)

    Deine KlassenZeiger.h Idee ist umsetzbar, aber nicht so wie du es geschrieben hast. Korrekt verwendet man dazu eine sog. "forward declaration", und die sieht so aus:

    class Klasse;
    


  • @hustbaer sagte in Klassen: privat Funktion/Variable "verstecken":

    (EDIT: War der Link zur "one definition rule" immer schon in @manni66 s Beitrag? Wenn ja hab ich wohl nach "Es ist falsch" aufgehört zu lesen und mein Hirn ausgeschaltet. Hmmm...)

    Nein, ich habs kurz vor deinem Post ergänzt.



  • @manni66
    Danke. Dann bin ich mal beruhigt 🙂
    Ich bin zwar von Haus aus schusselig, und jünger werde ich auch nicht, aber das übersehen zu haben hätte ich dann schon etwas krass gefunden.



  • Danke erst mal für die hilfreichen Antworten.

    Und danke für den Hinweis mit "class Klasse;" anstelle von "class Klasse {};".
    Eun gutes beispiel dafüur, warum ich euch hierim Forum Frage und nicht den Compiler.
    Der Compiler würde mir in dem Fall nur sagen "geht nicht", das es jedoch "nur" falsch geschrieben ist, merke ich erst, wenn ich bereits weiß das es eigentlich Funktionieren sollte und darum den Fehler suche, anstatt einfach anzunehmen es sei grundsätzlich "falsch".

    Was jedoch die Sache mit den Headerfiles/"OneDefinitionRule" betrifft, ist mir das Ganze jedoch immer noch ein wenig unklar (zumal ich selbst schon bei deutschen Texten so manches falsch verstehe).

    Zitat: In general, a translation unit shall contain no more than one definition of any class type. In this example, two definitions of the class type C occur in the same translation unit. This typically occurs if a header file is included twice by the same source file without appropriate header guards.

    Möglicherweise wurde meine Überlegung ja einfach nur missverstanden....Edit: Fehler Erkannt =daher=> unwichtige dumme Erklärung gelöscht:

    @axam sagte in Klassen: privat Funktion/Variable "verstecken":
    Möchte mich jetzt mal bei allen hier entschuldigen.
    Man sollte immer erstmal denken bevor man spricht.



  • Ein simples Beispiel um das mit der Deklaration und der einmaligen Definition zu erklären.

    // example.h
    class Example {
        int value_;
    public:
        Example (const int value);
        int getValue() const;
    };
    

    Und die dazu gehörende Implementation.

    // example.cc
    Example::Example (const int value) : value_ (value) {}
    
    int
    Example::getValue () const {
        return value_;
    }
    

    In der Datei example.cc stehen nun die Definitionen für den Konstruktor und für eine member function getValue.



  • @axam sagte in Klassen: privat Funktion/Variable "verstecken":

    Möglicherweise wurde meine Überlegung ja einfach nur missverstanden.

    Nein! Eine Klassendeklaration ist auch eine -definition. Die muss im gesamten Programm gleich sein.



  • @manni66 sagte in Klassen: privat Funktion/Variable "verstecken":

    Nein! Eine Klassendeklaration ist auch eine -definition.

    ?

    class test;
    


  • @Belli sagte in Klassen: privat Funktion/Variable "verstecken":

    @manni66 sagte in Klassen: privat Funktion/Variable "verstecken":

    Nein! Eine Klassendeklaration ist auch eine -definition.

    ?

    class test;
    

    Forward declaration



  • @manni66
    Ich glaube hier gibt es eine kleine Begriffsverwirrung.
    Das:

    class Foo {
    public:
        int bar();
        int baz();
    };
    

    ist eine vollständige Klassendefinition. Bloss die Member-Funktionen sind noch nicht definiert (aber bereits deklariert).



  • @axam Dein Hauptproblem ist, daß Du kein aktuelles Lehrbuch hast. C++ innerhalb eines Forenthreads zu vermitteln ist zum Scheitern verurteilt.

    // edit: blubb.

    // edit 2: @john-0 Du hast nicht verstanden ab wann eine Klasse definiert ist. +1 für @hustbaer ;

    @axam Vielleicht möchtest Du mal erklären was Du überhaupt erreichen möchtest? Visibility sollte nach meinem Link oben ja hoffentlich klar sein?



  • @axam
    Die Definition einer Klasse muss immer und überall (im ganzen Programm) gleich aussehen. Nicht auf den Buchstaben genau gleich, aber sie muss äquivalent sein. (Die genauen Regeln dazu welche Unterschiede erlaubt sind und welche nicht sind kompliziert und selten hilfreich, am besten gehst du davon aus dass jeder Unterschied verboten ist.)

    Und weiters gibt es noch die Regel dass eine Klasse pro Translation Unit nur 1x definiert werden darf. Das ändert aber nichts an der ersten Regel: wenn du 2 Translation Units hast, darf die Klasse natürlich pro TU 1x definiert werden, aber diese Definitionen müssen wie gesagt äquivalent sein.

    Das ist im übrigen auch ein Fehler den dir der Compiler nicht mitteilen wird. Also wenn du in zwei verschiedenen Files die selbe Klasse unterschiedlich definierst, dann kann es leicht sein dass du keine Fehlermeldung bekommst, dafür aber ein Programm das undefiniertes Verhalten hat. Also z.B. falsch rechnet oder abstürzt oder halt einfach irgendwas macht.



  • Möchte mich jetzt mal bei allen hier entschuldigen.
    Man sollte immer erstmal denken bevor man spricht.
    Hab mich da irgend wie in einen unsinnigen Gedanken verrannt, und mir ist inzwischen auch selbst klar geworden, warum soetwas nicht gehen kann und daß dies zu nichts führt.

    Trotzdem vielen Dank.
    Alerdings war dieses threat für mich dennoch sehr nützlich, denn vermutlich ist "PImpl Idiom" genau das, was ich gesucht habe.
    Zudem ist mir klar geworden, daß ich (geistig) nicht den Fehler machen darf eine Klasse mit .cpp und .h zu assoziieren (was jetzt im Nachhinein auch logisch ist, da mir ja bereits bekannt war, das ich ein Klasse auch direkt in die main.cpp schreiben könnte).

    Und @john-0, ein aktuelles Lehrbuch hätte mir bei meinem letztgenannten Denkfehler wohl auch nicht weiter geholfen, schließlich habe ich bereits dutzende Erklärungen gelesen, und mir ist trotzdem erst jetzt klar geworden, das ich von falschen Annahmen ausgehe.
    Werde mir jedoch in Zukunft etwas mehr Zeit lassen, bevor ich "dumme" fragen stelle, den wenn man erstmal eine Nacht darüber schläft, sieht die Welt plötzlich ganz anders aus.



  • @Swordfish sagte in Klassen: privat Funktion/Variable "verstecken":

    // edit 2: @john-0 Du hast nicht verstanden ab wann eine Klasse definiert ist. +1 für @hustbaer ;

    Wie immer ist es erbauend, wenn man einen Fehler in der Beschreibung gemacht hat, dass einem das notwendige Wissen abgesprochen wird.

    Also noch einmal sprachlich präzise.

    // example.h
    class Example; // Eine reine Deklaration einer Klasse, aber außer für opaque Zeiger vollkommen nutzlos.
    
    // Definition der Klasse, bis auf die member functions
    // Eine Definition kann nachher nicht mehr erweitert werden,
    // d.h. alle member müssen bei der Definition angegeben werden.
    // Nur member functions können später definiert werden.
    class Example {
        int value_;
    public:
        Example (const int value);
        int getValue () const;
    };
    
    // example.cc
    Example::Example(const int value) : value_ (value) {}
    
    int
    Example::getValue () const {
        return value_;
    }
    

    Wenn man stattdessen eine Template definiert, müssen die member functions auch direkt mit definiert werden. (Deshalb @hustbear würde ich nicht von einer vollständigen Definition reden, wenn die member functions nicht auch definiert wurden.)

    //example_template.h
    template <typename T>
    class Example {
        T value_;
    public:
        Example (const T& value) : value_ (value) {}
        T getValue () const {
            return this->value_;
        }
    }; 
    

    Nur Spezialisierungen können in einer eigenen Übersetzungeinheit definiert werden.

    Kommen wir zum eigentlichen Problem zurück. C++ erlaubt es nicht, die Definition einer Klasse später zu erweitern. D.h. die Klasse ist immer komplett sichtbar, aber member oder member functions sind nicht zugreifbar, wenn sie mit private oder protected geschützt sind. Das ist dann ein Problem, wenn man Implementationsdetails verstecken will. In C++ geht das im Gegensatz zu anderen OOP-Sprachen nicht, so dass man hier zum Proxy-Pattern bzw. Bridge-Pattern (auch PImpl-Idiom genannt) greifen muss.


Log in to reply