Freund erbt von privater nested class -> geht nicht



  • namespace x
    {
      class FooBar;  //forward-declaration
    
      class Foo
      {
      private:
        class Bar {};
    
       friend class ::x::FooBar;
      };
    
      class FooBar
      :private Foo::Bar 
      {
      };
    }
    

    Ich bekomme einen Fehler, dass Foobar nicht auf Foo::Bar zugreifen kann (von Comeau und VC 8.0; VC 7.1 hat das geschluckt). Aber warum bekomm ich den Fehler?

    FooBar ist doch ein Freund von Foo?



  • weil du versuchst von einer privaten klasse (Bar) zu erben ?



  • Ich weiß was ich tue, versteh aber nicht warum es nicht geht. Schließlich ist FooBar in Foo als friend declariert und sollte damit zugriff auf die privaten Element von Foo haben.



  • Ich kann (fast) denselben Effekt - den ich unter VC7.1 hatte - folgendermaßen nachbauen:

    namespace x
    {
      class FooBar;  //forward-declaration
    
      class Foo
      {
      public:
        class Bar 
        {
        private:
          ~Bar();
          friend class ::x::FooBar;
        };
      };
    
      class FooBar
      :private Foo::Bar   
      {
      };
    
      class FooBarEnemy
      :private Foo::Bar        //geht leider jetzt auch
      {
      };
    
      FooBar fooBar;
      FooBarEnemy fooBarEnemy; //das bringt den gewollten Fehler
    }
    

    Leider kriegt man den erwünschten Compilerfehler bei unerlaubter Vererbung dann aber erst, wenn man von FooBarEnemy eine Instanz anlegt.
    Wenn der Feind also nur statische Methoden hat, kann er geerbte statische Methoden verwenden. 😞 😉



  • weil FooBarEnemy nicht ~Bar() aufrufen kann da privat (nur vermutung)



  • Deine Vermutung ist schon richtig.

    Mich interessiert aber vielmehr die Frage:

    Wie kann ich sicherstellen, dass nur eine bestimmte Klasse B von einer Klasse Klasse A ableiten kann?

    Das hatte ich unter VC7.1 mit den privaten nested-Classes so, nur scheint das weder in VC++ 8 zu funktionieren noch Standard zu sein (verlass mich mal auf Comeau).

    Das mit dem privaten Ctor gefällt mir nicht, da ich damit nicht verhinder kann, dass X ableitet sondern nur, dass X instanziiert wird.



  • kartoffelsack schrieb:

    Ich bekomme einen Fehler, dass Foobar nicht auf Foo::Bar zugreifen kann (von Comeau und VC 8.0; VC 7.1 hat das geschluckt). Aber warum bekomm ich den Fehler?

    Weil der Name Foo::Bar im globalen Scope nicht sichtbar ist und genau dort referenzierst du ihn.

    Die privaten Elemente einer Klasse sind außerhalb einer Klasse nicht sichtbar, können also nicht per Name angesprochen werden. Ausnahme: Entitäten die als Freund deklariert wurden. Solche sehen die privaten Namen.

    In deinem Fall hast du die Klasse FooBar als Freund der Klasse Foo deklariert. Im Scope von FooBar kannst du also alle privaten Elemente von Foo sehen. Die Basisklassenliste einer Klasse gehört nun aber nicht zum Scope der Klasse. Aus diesem Grund kannst du in der Basisklassenliste von FooBar auch nicht auf die privaten Elemente von Foo zugreifen. Dies geht erst innerhalb der Klassendefinition.



  • Gibts ne Möglichkeit mein Ziel anders zu erreichen?

    Ich will beim Entwurf von Bar festlegen, wer ableiten darf?

    (Eigentlich will ich das Projekt von VC7.1 auf 8.0 umstellen, ohne etwas an der Struktur zu ändern. Ich könnte Bar auch einfach public machen, dann würde das Programm wieder übersetzen. Dann ist das ganze Code-Konstrukt aber völlig sinnlos.)



  • kartoffelsack schrieb:

    Gibts ne Möglichkeit mein Ziel anders zu erreichen?

    Eine wasserfeste fällt mir im Moment nicht ein. Alle Template-Lösungen die mir einfallen würden nicht gegen mutwillges Missverhalten schützen.

    Ich will beim Entwurf von Bar festlegen, wer ableiten darf?

    Da stellt sich für mich erstmal die Frage: Warum? Wenn man die genaue Motivation kennt, kann man vielleicht auch einen Workaround finden. Wenn Bars Quellcode bekannt ist und nur z.B. der Code von Bar nicht allgemein genug ist (viele Annahmen, wenig Tests...), dann sollte es doch reichen, wenn Bar seine Freunde benennt. Zusammen mit einem privaten Destruktor hat man dann eine kontrollierte Instanziierbarkeit. Machst du auch Bars statische Member alle private kann ein Non-Friend auch nicht durch "böswilliges" Erben darauf zugreifen.

    Letztlich stellt sich imo aber die Frage, ob du fremden Code wirklich als "Feind" betrachten musst. Die Kapselmechanismen sind ja eigentlich nicht als Schutz vor gewolltem Missverhalten gedachten.



  • Da stellt sich für mich erstmal die Frage: Warum?
    

    Die Motivation bei erstellen des Codes war eine kompakte Core-Klasse mit schlanker Schnittstelle zu haben, die aber nicht direkt angesprochen wird, sondern über eine zweite Schnittstellenklasse, die ein relativ reiches Interface zur Verfügung stellt.

    Das sorgt zum einen Dafür die eigentliche Klasse schlang zu halten - mit allen Vorteilen - eine konfortable benutzung aber über eine Klasse mit Member-Funktionen statt non-friend-non-Member-Klassen zu ermöglichen.

    Das ganze hat aber den Nachteil, dass man ziehmlich viel redundanden Code bekommt mit Funktionen, die die Aufrufe 1 zu 1 an andere Funktionen weiterleiten.

    Ich will dieses Design an dieser Stelle aber auch garnicht in Frage stellen oder gut heißen, ich will erstmal nen Port auf das neue VC ohne größeren Umbauten machen. Wenn das läuft kann man weiterdenken. Ich fürchte aber, dass ich jetzt erst weiterdenken und Umbauen muss und dann erst den Port machen kann 🙄 Das stellt das Vorhaben des Ports infrage (bzw. verschiebt es, bis nach meinem Ausscheiden aus meiner jetzigen Firma in ein paar Wochen 😃 )


Anmelden zum Antworten