Nameskonflikt



  • Folgender Code will nicht so richtig :

    namespace foo{
    	int bar(){}
    };
    
    class Base{
    private:
    	short bar;
    };
    
    class Derived:public Base{
    public:
    	void test(){
    		using namespace foo;
    		int a = bar();
    	}
    };
    

    GCC meint dazu :

    error: short int Base::bar' is private error:((Base*)this)->Base::bar' cannot be used as a function

    Ist das ein Fehler im GCC oder soll das so sein?

    Die Version ist:

    g++ (GCC) 3.4.2 (mingw-special)



  • namespace foo{
    	int bar(){}
    };
    
    class Base{
    protected: //nicht private, denn private ist numal privat ;) Nur falls (1) zutrifft
    	short bar;
    };
    
    class Derived:public Base{
    public:
    	void test(){
    		using namespace foo;
    		int a = bar; //(1)bar ist keine Funktion, sondern ne Variable
    		//oder du willst wirklich die Funktion bar() aufrufen, dann:
    		int a = foo::bar();
    	}
    };
    

    Ist das ein Fehler im GCC oder soll das so sein?

    nö, 1 oder 2 Fehler in deinem Code 😉

    Edit: Der C++ Compiler nimmt immer des an, was grad im Scope ist und der Bezeichner bar aus Base überlagert aufgrund der Vererbung immer den Bezeichner bar aus dem Namespace foo.



  • Natürlich wäre es ein Fehler wenn Base::bar protected wäre. Ist es aber nicht. Wenn ich foo::bar benutzen wollte, hätte ich den Namesraum wohl nicht gedumpt.

    In der fraglichen Zeile ist nur foo::bar zugänglich also sehe ich nicht wieso er meckert.

    Fals dieses Verhalten wirklich so sein sollte dann würde das bedeuten, dass man durch das verändern der privaten Teile einer Basisklasse abgeleitete Klassen brechen kann.



  • ko, nochmal. Ich nehme an du willst die Funktion bar im namespace foo aufrufen. Da aber bereits in der Klasse derived ein Objekt namens bar deklariert ist, überdeckt dieses in jedem Fall die Funktion bar aus foo (egal wie oft du using namespace foo angibts). Nun nimmt gcc an, dass du eben das bar aus Base ansprechen willst. Deshalb kommt zum einen der Fehler, dass bar private ist (darauf kann halt ne abgelittene Klasse net zugreifen) und das bar keine Funktion ist.
    Wenn du nun die Funktion bar() aufrufen willst, musst du explizit den Namespace in dem sie sich befindet angeben, d.h. foo::bar() aufrufen, an Stelle von bar().

    Der C++ Compiler kann ja von Haus aus net wissen, welchen Bezeichner du jetzt gemeint hast (bar aus Base oder bar aus foo), d.h. gibt es eben best. Regeln, welcher Bezeichner zuerst herangezogen wird. In dem Fall wird halt bar aus Base zuerst herangezogen und gcc erkennt, dass das B00lshit is ne Variable auf die Art und Weise zu verwenden.



  • bluecode schrieb:

    ko, nochmal. Ich nehme an du willst die Funktion bar im namespace foo aufrufen...

    Was da passiert weiß ich. Der GCC bringt diesbezüglich ja eine recht eindeutige Fehlermeldung. Die Frage war ob dieses Verhalten standardkonform ist.

    bluecode schrieb:

    Der C++ Compiler kann ja von Haus aus net wissen, welchen Bezeichner du jetzt gemeint hast

    Und ob er das kann! Derived kann per Definition nicht auf die privaten Elemente von Base zugreifen. Also dürfte Base::foo gar nicht einmal in Betracht kommen.

    Fals dieses Verhalten wirklich korrekt ist dann wechsele ich zu m_ aus technischen Gründen.



  • Ben04 schrieb:

    Fals dieses Verhalten wirklich korrekt ist dann wechsele ich zu m_ aus technischen Gründen.

    Imho ist das standardkonform, aber kA wo dein Problem damit ist 🙄 Ist aus dem Beispiel auch net ersichtlich...



  • Das Problem, das ich damit habe ist, dass man eine Abgeleiteteklasse brechen kann dadurch, dass man die Implementation der Basisklasse durch eine equivalente ersetzt was die Kapselung zerstört.

    void foo(){}
    
    class A{
    public:
      void set_dingsda(int new_bar){ bar = new_bar; }
    private:
      int bar;
    };
    
    class B:public A{
    public:
      void prob(){
        foo();
      }
    };
    

    So nun begint man A zu dokumentieren und stellt fest, dass es besser wäre wenn bar foo heißen würde da dies ein aussagekräftiger Name ist. bar ist nicht Teil der Schnittstelle da private, also sollte die Veränderung nur klasseninterne Auswirkungen haben.

    void foo(){}
    
    class A{
    public:
      void set_dingsda(int new_foo){ foo = new_foo; }
    private:
      int foo;
    };
    
    class B:public A{
    public:
      void prob(){
        foo();
      }
    };
    

    Falsch gedacht. B wird dadurch gebrochen.

    B kann durchaus eine Klasse ganz wo anders sein und von jemand anderem gewartet werden, der dann mal ordentlich anfängt zu fluchen, wieso jetzt auf einmal Code, der vorher einbahnfrei funktionierte, nicht mehr will.

    Das heißt also entweder sämtliche Member pre- oder postfixen oder massiv Pimpl einsetzen um Namenskonflikte zu vermeiden. Beides nicht sonderlich elegant. 😞



  • Es hilft schon, den Sachen sinnvolle Namen zu geben. Dann kann ne Variable niemals so heißen wie eine Funktion.

    Und selbst wenn: Dann macht man halt per Search & Replace ne volle Qualifikation und die Sache ist gegessen.


  • Mod

    das problem sind eigentlich nicht namenskonflikte in basisklassen etc. sondern der relativ schwache bezug, den eine using direktive bewirkt. persönlich verwende ich sie eigentlich nie - wenn bezeichner aus anderen namensräumen gebraucht werden und ADL nicht genutzt wird, muss es eben ein qualifizierter bezeichner sein (evtl. mit namespace-alias verkürzt) oder ein per using deklaration sichtbarer. letztere variante hat dieses problem nicht.



  • Ben04 schrieb:

    bluecode schrieb:

    Der C++ Compiler kann ja von Haus aus net wissen, welchen Bezeichner du jetzt gemeint hast

    Und ob er das kann! Derived kann per Definition nicht auf die privaten Elemente von Base zugreifen. Also dürfte Base::foo gar nicht einmal in Betracht kommen.

    Fals dieses Verhalten wirklich korrekt ist dann wechsele ich zu m_ aus technischen Gründen.

    Das Problem bei dieser Begründung ist aber, daß Sichtbarkeit (welche Namen sind verfügbar?) und Schutzbedingungen (auf welche Elemente darf ich zugreifen?) unabhängig voneinander geprüft werden - zuerst wird festgestellt, welche Objekte sichtbar sind, danach werden die Zugriffsrechte auf diese Objekte kontrolliert.



  • Hallo zusammen,

    vielleicht wirds klarer, wenn man sich mal bewusst wird, was Vererbung eigentlich bedeutet. Alles von der Basisklasse wird weitervererbt. Auch die privaten Elemente. Diese sind zwar nicht sichtbar, aber trotzdem in der Kindklasse vorhanden.

    Es ist also quasi EIN Objekt, auch wenn Vererbung und zwei Klassen im Spiel sind.

    Daher versucht der Compiler immer zuerst auf die eigenen Variablen zuzugreifen. Das ist aber eine private Methode, und daher kann er nicht darauf zugreifen.

    Dasist kein Verstoß gegen OO, sondern ein OO-Designfehler.

    Ich weiß nicht ob es die Möglichkeit in C++ gibt, aber z.B. in Java oder auch in C# ist es möglich mit dem Schlüsselwort "new" neue Methoden/Variablen einzuführen, und die "alten" zu vergessen. Aber auch das ist nicht die "feine Art".

    Gruß
    Norman-Timo


Anmelden zum Antworten