Kein protected-Zugriff möglich



  • Ich war bislang immer davon ausgegangen, daß der Zugriffsschutz auf Klassenebene gilt, aber nun versucht mich mein Compiler vom Gegenteil zu überzeugen:

    class base
    {
    	int val;
    protected:
    	int get_val() const {return val;}
    	void set_val(int i) {val=i;}
    };
    
    class derived : public base
    {
    public:
    	void set_base(const base& other)
    	{ set_val(other.get_val()); }
    	void set_data(int i)
    	{ set_val(i); }
    };
    

    liefert die Meldung:

    test.h(13) : error C2248: "get_val" : Kein Zugriff auf protected Element, dessen Deklaration in der Klasse "base" erfolgte
    test.h(5) : Siehe Deklaration von 'get_val'

    base::get_val() ist protected, also sollte die abgeleitete Klasse derived imho auch darauf zugreifen können. Allerdings meldet mir mein Compiler, daß er keinen Zugriff bekommt (bei der set_val()-Methode klappt es dagegen ohne Probleme).

    Wer hat nun recht - ich oder der Compiler?



  • Hallo

    Der Unterschied zwischen den beiden fraglichen Methoden ist ja, das bei set_base ein "fremdes" Objekt bearbeitet werden soll. Der Compiler verweigert das, vermutlich mit dem Hintergedanken : Es könnte ja ein Objekt übergeben werden, das von einer Klasse abgeleitet von Base ist. Diese potentielle Klasse X kann aber ganz eigene Mechanismen haben, die protected-Methoden von Base zu behandeln. Durch die set_base-Methode aber würde diese Behandlung von X aber außer Kraft gesetzt, wenn der Compiler das nicht verhindern würde.

    bis bald
    akari



  • akari schrieb:

    Es könnte ja ein Objekt übergeben werden, das von einer Klasse abgeleitet von Base ist.

    Ja, in dem Zusammenhang soll sogar ein Objekt einer abgeleiteten Klasse übergeben werden (im Original ist die Methode pur virtual, aber das ändert nichts an den Fehlermeldungen). Mein Hintergedanke war, diese Methode für einen internen Datenaustausch zwischen verschiedenen Klassen nutzen zu können.

    Diese potentielle Klasse X kann aber ganz eigene Mechanismen haben, die protected-Methoden von Base zu behandeln. Durch die set_base-Methode aber würde diese Behandlung von X aber außer Kraft gesetzt, wenn der Compiler das nicht verhindern würde.

    Wieso sollte eine andere (womöglich gar nicht existierende) Klasse ein Problem damit haben, daß ich über eine wohldefinierte Schnittstelle auf ihre Daten zugreifen kann? (zumal der Zugriff nur Bestandteile der Basisklasse betrifft)



  • CStoll schrieb:

    ...

    class base
    {
    	int val;
    protected:
    	int get_val() const {return val;}
    	void set_val(int i) {val=i;}
    };
    
    class derived : public base
    {
    public:
    	void set_base(const base& other)
    	{ set_val(other.get_val()); }
    	void set_data(int i)
    	{ set_val(i); }
    };
    

    ...

    😮 😮
    Ich hätte auch meine Hand dafür ins Feuer gelegt, dass das funktioniert.

    interessanterweise funktionert aber:

    class base
    {
    	int val;
    protected:
    	int get_val() const {return val;}
    	void set_val(int i) {val=i;}
    };
    
    class derived : public base
    {
    public:
    	void set_base(const derived& other) // "derived" !
    	{ set_val(other.get_val()); }
    	void set_data(int i)
    	{ set_val(i); }
    };
    

    Gruß,

    Simon2.



  • Vielleicht könnte das Statement von Jason ganz interessant für sein 😉

    http://groups.google.de/group/microsoft.public.vc.language/browse_thread/thread/38fd5d3c3a9350c9/d01e53b455cb9360?lnk=st&q=error+C2248%3A+protected&rnum=1&hl=de#d01e53b455cb9360

    Gruß,
    Baracke

    Edit: *hust* Deutsche Sprache schwere Sprache....



  • Baracke schrieb:

    Vielleicht könnte das Statement von Jason ganz interessant für sein 😉

    http://groups.google.de/group/microsoft.public.vc.language/browse_thread/thread/38fd5d3c3a9350c9/d01e53b455cb9360?lnk=st&q=error+C2248%3A+protected&rnum=1&hl=de#d01e53b455cb9360
    ...

    Jason Shirk schrieb:

    The compiler is correct. A derived instance can only access protected
    members via this. Because n is not this, the context for access checking is
    not that of the derived.

    If you aren't convinced, Borland, GCC, and Como all agree w/ VC, it's an
    error.

    Jason Shirk
    VC++ Compiler Team

    Hmmmm ... irgendwie bricht das mit einigem, was ich bislang über die Zugriffsrechte zu wissen glaubte ...

    Gruß,

    Simon2.



  • Simon2 schrieb:

    interessanterweise funktionert aber:

    ...
    

    Ja, das wußte ich selber, aber das ist leider nicht das, was ich benötige. Schließlich will ich als 'other' ja nicht nur derived-Objekte übergeben, sondern auch andere von base abgeleitete Objekte.
    (und die momentane Notfalllösung, get_val() public zu setzen, gefällt mir auch nicht)

    Simon2 schrieb:

    Hmmmm ... irgendwie bricht das mit einigem, was ich bislang über die Zugriffsrechte zu wissen glaubte ...

    Ja, das ging mir genauso 😉



  • Hallo,
    darüber kann man leicht stolpern. Der Standard ist hier aber eindeutig. Er legt fest, dass ein Member/Friend einer abgeleiteten Klasse nur über Pointer/Referenzen/Objekte vom Typ abgeleitete Klasse auf die protected-Member der Basisklasse zugreifen kann. Der Grund für diese Einschränkung ist statische Typsicherheit. Gäbe es diese Einschränkung nicht, könnte eine abegeleitete Klasse andere abgeleiete Klassen kaputt machen (weil die z.B. ein protected-Member anders interpretieren).

    Ein schönes Beispiel von Jim Hyslop:

    class animal {
    protected:
       point3d whereAmI;
    // ... etc.
    };
    
    class fish : public animal {
    // whatever
    };
    
    class bird : public animal {
    public:
       // birds of a feather...
       void flockTogether(animal &a) const { a.whereAmI = whereAmI; }
    };
    
    int main() {
       fish f;
       bird b;
       b.flockTogether(f); // Oops, the bird just drowned.
    }
    

    Oder ein anderer Klassiker:

    class Shape {
    public:
    	// ...
    protected:
    	void setHeight(int h) {
              height_ = h;
    	}
    	void setWidth(int w) {
    	  width_ = w;
    	}
    private:
    	int height_;
    	int width_;
    };
    
    class Square : public Shape {
    public:
    	void resize(Shape& s) {
    		s.setHeight(42);
    		s.setWidth(42);
    	}
    };
    class Rectangle : public Shape {
    public:
    	void resize(Shape& s) {
    		s.setHeight(42);
    		s.setWidth(8);
    	}
    };
    
    int main() {
    	Square s;
    	Rectangle r;
    	r.resize(s); // Ups! 
    }
    


  • Erstaunlich, daß C++ sich so gegen mögliche Designfehler stemmt, aber ist das nicht ein wenig radikal gelöst? Und vor allem: Wie kann ich die Sache angehen, wenn ich wirklich über die Basisklasse auf eine protected Methode zugreifen will?

    Zur Untermalung die ungefähre Struktur in meiner Anwendung:

    //Basisklasse (abstrakt)
    class base_filter
    {
    public:
      virtual double calc(...)=0;
    protected:
      virtual base_filter* clone()const=0;//kopiert einen Filter
    };
    
    //Composite - Verknüpfung mehrerer Filter
    class composite_filter
    {
      vector<base_filter*> members;
    public:
      insert(const base_filter& filt)
      {
        members.push_back(filt.clone());// <- hier kommt C2248
      }
    
      virtual double calc(...)
      {
        double ret=0;
        for(size_t i=0;i<members.size) ret+=members[i]->calc(...);
        return ret;
      }
    };
    

    Der composite_filter speichert jeweils eine Kopie der eingefügten filter, weil ich dort auch temporäre Werte reinpacken und die Filter über Funktionsgrenzen weiterreichen will (Stichwort: "virtueller Konstruktor"). Aber ich möchte den Zugriff auf die clone()-Methode für Außenstehende verbieten.



  • Hallo,
    hier hilft wie immer die gute alte Indirektion: du hast keinen Zugriff auf clone() über filt. Dafür aber auf die protected-Methoden von base_filter über this. base_filter kann auf alle Member aller base_filter-Objekte zugreifen.
    Ergo:

    #include <vector>
    using namespace std;
    class base_filter
    {
    public:
      virtual double calc()=0;
    protected:
      virtual base_filter* clone()const=0;
      // Neu - erzeuge clone von c
      base_filter* clone(const base_filter& c) {
         return c.clone();
      }
    };
    
    //Composite - Verknüpfung mehrerer Filter
    class composite_filter : public base_filter
    {
      vector<base_filter*> members;
    public:
      void insert(const base_filter& filt)
      {
        // ok: clone filt über Base::clone(c)
        members.push_back(clone(filt));
      }
    };
    


  • Thx, so funktioniert es tatsächlich

    oder - wie ein Kollege neulich sagte:

    TactX schrieb:

    Man lernt nie aus 😞 😉



  • CStoll schrieb:

    Simon2 schrieb:

    interessanterweise funktionert aber:

    ...
    

    Ja, das wußte ich selber, aber das ist leider nicht das, was ich benötige....

    Das war auch nicht als Lösung gedacht, sondern grenzt vielleicht ein, wo ich falsch denke ...
    (den zitierten Spec-Verweis, aus dem genau das hervorgeht, habe ich erst danach gefunden (Kap. 11.5))

    Gruß,

    Simon2.



  • HumeSikkins schrieb:

    ...
    Ein schönes Beispiel von Jim Hyslop:

    class animal {
    protected:
       point3d whereAmI;
    // ... etc.
    };
    
    class fish : public animal {
    // whatever
    };
    
    class bird : public animal {
    public:
       // birds of a feather...
       void flockTogether(animal &a) const { a.whereAmI = whereAmI; }
    };
    
    int main() {
       fish f;
       bird b;
       b.flockTogether(f); // Oops, the bird just drowned.
    }
    

    (der Kommentar ist wohl falsch: Der Vogel ist nicht ertrunken, sondern der Fisch fliegt) 😉

    Erst dachte ich: Hmmm, aber das Beispiel finde ich unbefriedigend - das sieht mehr eher nach einem "const-"-Thema aus als nach "access specification". Wenn flockTogether() einen non-const-Parameter bekommt, kann es den natürlich ändern ... ist doch irgendwie nichts Besonderes.

    ... aber mit ein wenig Nachdenken ist es schon klar: Wenn ich protected-Elemente habe, gehe ich davon aus, dass "ich" die ändern kann ... nicht aber, "Geschwister, Onkels und Cousins".

    Anders gesagt: Wenn das anders wäre, würde folgender Hack mit Standardmittel funktioneren:
    Guter Programmierer:

    class base
    {
    protected:
       int val;
    };
    
    class derived : public base
    { 
    public:
       derived(int i) { val = i;}
    // legt val "NICHT nach außen"
    };
    

    Böser Programmierer:

    template <typename BaseT, typename DerivedT, typename VarType>
    class hack : public BaseT {
    public:
       typedef VarType DerivedT::*dMember;
       static VarType& get(DerivedT& d, dMember dMem) { 
          return d.*dMem;
       }
    };
    
    int main(void)
    {
       derived d(2);
       hack<base, derived, int>::get(d, &derived::val) += 3; 
    // Verändert d.val, obwohl nur derived-Objekte das sollten
    // (hat sich der derived-Entwickler eben so ausgedacht)
    }
    

    Danke,

    Simon2.


Anmelden zum Antworten