Komplizierte Frage zu Vererbung und Zugriff



  • Moin!

    Habe eine Frage zu folgendem Konstrukt:

    class A {
     protected:
     FILE* fp;
    };
    
    class B : public A {
      public:
      friend class C; // OHNE DIESE ZEILE GEHT UNTEN DAS ERSTE fread() NICHT!
      class B_Entry {
      public:
      virtual void read(B* b_class) = 0;
      //...
      };
    };
    
    class C : public B {
     public:
     class C_Entry : public B::B_Entry {
      public:
      void read(B* b_class);
      //..
     };
    };
    

    Das Prob liegt in der Implementation:

    void C::C_Entry::read(B* b_class)
    {
     //...
     fread(/*...*/ , b_class->fp); // geht nicht, compiler-error!
     fread(/*...*/ , ((C*)b_class)->fp); // geht komischerweise
    }
    

    Der Kompiler sagt bei der ersten fread()-Zeile:

    error: ‘FILE* A::fp’ is protected
    

    Muss ich das verstehen? 😉
    Bitte um Hilfe!

    Mfg,
    voidpointer



  • class B_Entry { 
      virtual void read(B* b_class) = 0;
    

    public vergessen?



  • oh sry, ist natürlich public, hatte mich im forum verschrieben



  • OK habe die friend Deklaration (oben mit Kommentar markiert) hinzugefügt, dann geht es. Aber was hat das mit einer Freundschaft zwischen B und C zu tun, wenn ich doch auf ein Element von A (A::fp) zugreifen möchte?


  • Mod

    protected existiert nicht, damit sich eine Klasse auf Umwegen zum friend erklären kann (indem sie einfach ableitet) - Freundschaft wird gewährt nicht gefordert. protected dient dazu, einem Objekt Zugriff auf ihre eigene Implementation, die z.T. in einer Basisklasse erfolgt ist, zu gewähren. Folglich ist bei nicht-statischen protected-Membern immer ein Objekt eigenen Klasse erforderlich.
    Angenommen, das wäre anders, betrachte:

    class D : public B { /* ... */ };
    C::C_Entry c;
    D d;
    c.read(&d); // ups
    

    Generell sind protected Datenmember problematisch und ein Bugmagnet. Vermeide sie.

    Edit: auch mit der friend-Deklaration wie oben beschrieben darf es nicht funktionieren (das ist also ein Compiler-bug). Freundschaft ist nicht transitiv, ein Member einer verschachtelten Klasse, kann nicht auf protected oder private Member einer Klasse zu greifen, die der umliegenden Klasse Freundschaft gewährt.
    Da Member einer verschachtelten Klasse keine besonderen Zugriffsprivilegien bzgl. der umschließenden Klasse haben (wird nicht von jedem Compiler beachtet), darf im Übrigen auch die zweite Form mit dem Cast nicht funktionieren.



  • OK, das hieße, dass du mir von protected abrätst und dass ich also alles mit friend-Deklarationen lösen müsste? Das Problem ist, dass ich in diesem Fall mehrere C-Klassen habe, und dann müsste ja A C1, C2, C3 ... und deren Subklassen (C1::Entry ... ) alle zu Freunden machen? Das wäre ja ein riesen Aufwand... gibt es keine sinnvollen Alternativen?

    Greetz


  • Mod

    Ich schlage nichts vor, weil der Zweck wegen der Bezeichner unklar bleibt.



  • Der Zweck ist, dass A praktisch eine Datei repräsentiert, B eine generelle Datei, die man zum als Verpackungsdatei nutzen kann (ZIP, TAR, ...) - also pure virtuell. C1, C2 etc. repräsentieren Klassen für die einzlnen Verpackungsdateiarten, z.B. ein Tar-File. Dabei ist C1::Entry ein Header, wie er in dann z.B. in einer Tar-Datei ist (Name der verpackten Datei, verpackte Länge, Rechte etc.)...

    Nun Sollen also die Entries in C1, C2... (ZIP, TAR) etc. von einem pure virtuellen B:Entry erben. Aber in der C::read()-Funktion gibt es, wie oben geschrieben, eben diese Probleme.


  • Mod

    Es würde genügen, wenn B_Entry friend von B ist und ein (protected) Interface zur Verfügung stellt, über das auf die Innereien von B definiert zugegriffen werden kann.



  • Hallo

    Also falls du mich hypothetisch fragen würdest was ich von dem Ganzen Konstrukt halte, würde ich meinen das Du das Thema verfehlt hast.
    !Alles das was camper in seinem "protected existiert nicht" post gemeint hat!
    Friends sind eigentlich vermeidbar.
    protected data types bringen schlechtes karma.
    geschachtelte Klassen finden niemals ihre Anerkennung. (ausser sie wollen es nicht => z.b. aggregation)

    Schau mal ob dir unter Umständen Templates bei deinem Problem helfen können. Ich kenne jedoch dein Problem zu wenig um da eine genaue Aussage treffen zu können.

    Aber mein Tip: C++ ist nicht immer gleich OOP. Löse erstmal dein eigentliches Problem. Es geht hier ja wahrscheinlich um sowas wie Datenkompression. Dann fasse zusammen.

    WX



  • mit Interface meinst du, dass ich z.B. dem Entry im Konstruktor einen Pointer auf B (generelle Verpackungsdatei) übergeben könnte, den es dann in einer protected-variable einspeichert?


  • Mod

    class B : public A {
      public:
      class B_Entry {
      public:
      virtual void read(B* b_class) = 0;
      protected:
      FILE* get_fp(const B*) const;
      //...
      };
      friend class B_Entry;
    }; 
    
    ...
    void C::C_Entry::read(B* b_class)
    {
     //...
     fread(/*...*/ , get_fp(b_class));
    }
    

    Das ist also nicht wesentlich anders als normale getter/setter, nur dass sich sich hier nicht in der gleichen Klasse wie die Daten selbst befinden.



  • @WX: nein, das OOP ist genau was ich wollte. Vorher war es auf funktionen basierend, aber das war mir irgendwie keine Perfektion.

    @camper: Danke, die Idee ist gut. Ich habe aber gleich den FILE-Pointer in den Protected-Bereich von B geschrieben anstatt eine get-Funktion, das ist ja schneller so. Oder gibt es Gründe, eine get-Funktion hier zu nehmen?


Anmelden zum Antworten