private bleibt wirkungslos



  • Gut danke, der Hinweis mit der Klassenebene laesst mich da klarer durchblicken - das hatte ich vorher wohl falsch (Objektebene) verstanden. :xmas2:



  • Für protected ist der Schutz allerdings auch auf Objektebene. Du kannst nur this->protectedMember benutzen.



  • Nein, Optimizer,
    auch andere Methoden der gleichen Klasse (sogar statische Methoden der Klasse) können mittels eines Objektzeigers auf protected Elemente des Objekts zugreifen (denn wenn es schon für private geht, dann für protected erst recht!).



  • @Fabeltier

    Es ist zwar kein Muss, aber du solltest binäre Operatoren besser auf Namensraumebene überladen und nicht auf Klassenebene.

    class Rational
    {
        //...
    };
    
    Rational operator*(const Rational& lhs, const Rational& rhs)
    {
        //...
    }
    

    Und sofern der Zugriff auf private Member notwendig ist, muss noch eine friend Deklaration innerhalb der Klasse stehen.

    class Rational
    {
        //...
        friend Rational operator*(const Rational& lhs, const Rational& rhs);
        //...
    };
    

    Das hat den Vorteil, dass hier Konstruktoren für _beide_ Operanden implizit herangezogen werden können, bei der Klassenimplementation ist das nur beim rechten Operand der Fall. Und normalerweise will man ja, dass sowohl 'a op b' als auch 'b op a' funktioniert.


  • Mod

    Shinja schrieb:

    Kam mir auch irgendwie konmisch vor, dass man scheinbar ueber ein Objekt einer Klasse auf private Elemente eines anderen Objekts dieser Klasse zugreifen kann.

    Kann man die eigentlich auch noch aendern (falls man aus versehen nicht als const uebergibt)?

    Irgendwie dachte ich, es sei logischer, nur das Objekt selbst und nicht Objekte der gleichen Klasse koennten auf private zugreifen. Aber dann wuerde man wohl Probleme mit de copyconstructor kriegen...

    Komisch finde ich, dass das z.B auch in Bruce Eckels Thinking in C++ nicht erwaehnt wird. Oder hab ichs ueberlesen?

    Wie meine Vorredner, hier noch ein Beispiel, um zu verdeutlich, wie ungünstig eine Kapselung auf Objektebene wäre:

    class Foo
    {
        int foo_;
    public:
    // ...
        int select_foo(const Foo& other) const
        {
            const Foo& r = rand() % 2 ? *this : other;
            return r.foo_;
        }
    };
    

    Offensichtlich könnte eine Verletzung auf Objektebene nur während der Laufzeit festgestellt werden und auch dann müsste man prüfen, ob nicht other zufällig auf dasselbe Objekt wie *this verweist. Jedenfalls ist eine Prüfung zur Laufzeit statt beim Compilieren etwas, dass wir gerade vermeiden wollen.



  • Soweit müsste man gar nicht gehen. Private/protected Zugriff auf Objektebene würde in C++ nur Sinn machen, wenn man prüft, ob ein Member zu this gehört oder nicht. In deinem Fall gehört foo_ zu r und müsste somit einen Compilerfehler verursachen. Die Laufzeitbindung von r ist daher nicht wirklich entscheidend.



  • @ groovemaster:
    Danke, das Ueberladen von Operatoren ausserhalb der Klassendeklaration kam in den weiteren Kapiteln 😉 und soweit ich mich noch erinnern kann an frueher, war das ja auch eine der wenigen "sinnvollen" Anwendungen von friend Deklarationen (soweit bin ich mom aber noch nicht) - gut, aber damit nerv ich dann in anderen Posts. Soweit so gut - vielen Dank fuer die Erklaerungen zum Thema "private"!



  • Ich danke ebenfalls. Wahrscheinlich ist es den meisten Leuten sofort ersichtlich gewesen als Anfänger, dass private eben für alle Objekte dieser Klasse gilt (eben Klassenbezogen) Ich habe da nicht genug drüber nachgedacht und hab intuitiv private als Objektbezogen interpretiert.

    Naja, habs dann ja später durch Copyconstructor und co rausgefunden. Bin aber froh, dass es mir wenigstens nicht alleine so ergangen ist.



  • Wenn da so 😉 :

    const Rational operator*(const Rational& lhs, const Rational& rhs);
    

  • Mod

    Freak_Coder schrieb:

    Wenn da so 😉 :

    const Rational operator*(const Rational& lhs, const Rational& rhs);
    

    Begründung?



  • Freak_Coder schrieb:

    Wenn da so 😉 :

    const Rational operator*(const Rational& lhs, const Rational& rhs);
    

    Durchaus möglich, mache ich meistens auch so. Ist aber keinesfalls zwingend. Genauso wenig wie die Argumente per Referenz zu übergeben. Alexandrescu hat zB mal was dazu geschrieben. Es ging hier ja lediglich um das Prinzip zwischen Klassen- und Namensraumüberladung von binären Operatoren. Die letztendliche Implementation hängt sowieso vom jeweiligen Szenario ab.



  • groovemaster schrieb:

    Soweit müsste man gar nicht gehen. Private/protected Zugriff auf Objektebene würde in C++ nur Sinn machen, wenn man prüft, ob ein Member zu this gehört oder nicht. In deinem Fall gehört foo_ zu r und müsste somit einen Compilerfehler verursachen. Die Laufzeitbindung von r ist daher nicht wirklich entscheidend.

    Es ist doch das selbe Objekt. r und this haben die selbe Adresse, zeigen auf den selben Speicherbereich, also sind es die selben Objekte. Es wird ja kein neues Objekt, sondern nur ein Alias für das selbe Objekt erzeugt.



  • groovemaster schrieb:

    Genauso wenig wie die Argumente per Referenz zu übergeben. Alexandrescu hat zB mal was dazu geschrieben.

    Warte mal. Alexandrescu bezog sich dabei auf eine Kopiersemantik. D.h. (wenn Du das gleiche meinst wie ich, wovon ich aber ausgehe) er hat keineswegs irgendwo geschrieben, dass man Argumente nicht per Referenz übergeben sollte -- außer für den Fall, dass man sie danach eh kopiert. Folgender Code ist natürlich ein wenig unsinnig:

    void foo(T const& v)
    {
        T tmp = v; // Kopie
        // ...
    }
    

    Dann hätte man nämlich gleich eine Kopie übergeben können. Aber ansonsten sollte man bei non-PODs durchaus immer Referenzen verwenden.



  • @Konrad
    Ja, das meinte ich. Aber mir ist nicht ganz klar, worauf du hinaus willst. Dein Beitrag steht doch in keinem Widerspruch zu meiner Aussage. Richtlinien sind nunmal nicht zwingend oder implizieren Allgemeingültigkeit.

    DEvent schrieb:

    Es ist doch das selbe Objekt. r und this haben die selbe Adresse, zeigen auf den selben Speicherbereich, also sind es die selben Objekte. Es wird ja kein neues Objekt, sondern nur ein Alias für das selbe Objekt erzeugt.

    Ich habe ja auch nicht gesagt, dass das, was camper geschrieben hat, falsch ist. Das Beispiel widerstrebt nur dem Design von C++, also dem statischen Typsystem und der daraus resultierenden statischen Fehlerbehandlung. Und auf der Basis, also wo Laufzeitbindung eine Rolle spielt, kann man eben keinen Memberzugriff auf Objektebene spezifizieren. Alles was bleibt, ist der Sichtbarkeitsbereich. Und dort spielt die Laufzeitbindung dann keine Rolle mehr.



  • camper schrieb:

    Begründung?

    Damit zB sowas nicht möglich ist:

    Rational a,b,c;
    //...
    a*b = c;
    

    Bei einem nicht-Konstanten Rückgabewert würde dies funktionieren...
    Aber ich denke mal du weißt das schon und willst mich jetzt eines besseren Belehren 😃


  • Mod

    Freak_Coder schrieb:

    camper schrieb:

    Begründung?

    Damit zB sowas nicht möglich ist:

    Rational a,b,c;
    //...
    a*b = c;
    

    Bei einem nicht-Konstanten Rückgabewert würde dies funktionieren...

    Aber ich denke mal du weißt das schon und willst mich jetzt eines besseren Belehren 😃

    Keineswegs. Ich meine, die Frage, ob man ein derartiges Konstrukt überhaupt verhindern muss, sollte jeder selbst entscheiden. Ich habe selten das Bedürfnis, so etwas zu schreiben.
    Wir können Vorkehrungen wegen Murphy treffen, aber gegen Machiavelli ist in C++ sowieso kein Kraut gewachsen.
    Immerhin, ein Konstruktion der Art

    a*b*=c
    

    an Stelle von

    a*b*c
    

    kann für eigene Typen durchaus sinnvoll sein, um ein paar unnötige Temporaries loszuwerden, und wir das ganze nicht gerade durch Expressiontemplates lösen.



  • Es geht nicht darum sowas verhindern zu müssen, sondern sicherzustellen das man nicht versehentlich sowas schreibt. Fehler basieren darauf, das man es nciht mit Absicht macht. Wann immer der Compiler in der Lage ist solche Fehler anzumeckern hat man sich selber stundenlandes debuggen erspart.

    Jeh genauer man die Bedingungen seiner Funktionen definiert (im Bezug auf public/private ebenso wie const correctness), desto mehr kann einem der Compiler dabei unterstützen Fehler zu verhindern. Für die reine Funktionalität sind die meisten dieser Sachen letzten Endes belanglos. Will sagen, Du kannst ebenso alles public machen und komplett auf const verzichten, laufen wird das Programm dann auch. Nur erhöhst Du damit die Chancen, daß Fehler unbemerkt bleiben... Bis sie dann in Produktion aufschlagen...



  • camper schrieb:

    Keineswegs. Ich meine, die Frage, ob man ein derartiges Konstrukt überhaupt verhindern muss, sollte jeder selbst entscheiden.

    Ja und wenn ich schon die Möglichkeit habe, solche Fehlerquellen zu ellimnieren, dann versuche ich es auch...

    a*b*=c
    

    Sowas finde ich unschön und das sieht ziemlich ungewoht aus 🙂

    Ich halt mich nur an das was Meyer in einem seiner Bücher mal geschrieben hat, ungefähr so: "Benutzerdefinierte Typen sollten sich so wie eingebaute verhalten".

    Und da dies bei int's und Konsorten auch nicht funktioniert, tue ich dies für meine Typen auch, schue hats eingentlich auf den Punkt gebracht.

    Aber jedem seine Entscheidung ...



  • Ich habe ja auch nicht gesagt, dass das, was camper geschrieben hat, falsch ist. Das Beispiel widerstrebt nur dem Design von C++, also dem statischen Typsystem und der daraus resultierenden statischen Fehlerbehandlung. Und auf der Basis, also wo Laufzeitbindung eine Rolle spielt, kann man eben keinen Memberzugriff auf Objektebene spezifizieren. Alles was bleibt, ist der Sichtbarkeitsbereich. Und dort spielt die Laufzeitbindung dann keine Rolle mehr.

    Ich versteh dich grade überhaupt nicht.

    Camper hat gesagt, wenn man die Sichtbarkeit auf Objektebene regeln könnte, dann kann man erst zur Laufzeit entscheiden ob ein Member sichtbar ist oder nicht. Du hast gesagt das braucht man nicht, da r nicht this ist und somit die Sichtbarkeit zur Compilerzeit entschieden werden kann.

    Aber r ist doch this. Ob eine Variable jetzt r oder this heißt ist doch egal, hauptsache sie zeigen beide auf den selben Speicherbereich und sind vom selben Typ.

    Also müsstest du fordern das this eine spezielle Variable ist, die einzigartig ist.

    int select_foo(const Foo& foo1, const Foo& foo2)
    {
        const Foo& r = rand() % 2 ? foo1 : foo2; 
        return r.foo_;
    }
    

    Wie willst du jetzt die Sichtbarkeit entscheiden?
    Edit: select_foo ist friend-Funktion der Klasse Foo.



  • DEvent schrieb:

    Camper hat gesagt, wenn man die Sichtbarkeit auf Objektebene regeln könnte, dann kann man erst zur Laufzeit entscheiden ob ein Member sichtbar ist oder nicht. Du hast gesagt das braucht man nicht, da r nicht this ist und somit die Sichtbarkeit zur Compilerzeit entschieden werden kann.

    Nein, hier bringst du was durcheinander. camper brachte ein Beispiel, was zeigt, wie man Zugriff auf Objektebene unter C++ nicht realisieren kann. Mein Punkt war lediglich der, dass dies unter C++ sowieso nicht zur Debatte steht, weil es nicht zum Konzept von C++ passt. Das einzige was man unter C++ machen kann, ist, den Zugriff auf Objektebene über den Sichtbarkeitsbereich zu regeln, anstatt sich auf Objektidentität zu versteifen.


Anmelden zum Antworten