Sind innere Klassen in C ++ automatisch Freunde?


73

Wenn ich eine innere Klasse in C ++ definiere, ist sie automatisch ein Freund der Klasse, die sie enthält? Ist das zum Beispiel legal:

class Outer {
public:
    class Inner {
    public:
        void mutateOuter(Outer& o);
    };

private:
    int value;
};

void Outer::Inner::mutateOuter(Outer& o) {
    o.value ++; // Legal?  Or not?
}

Ich frage, weil auf einigen Compilern, die ich ausprobiert habe (VS2003), dieser Code nicht funktioniert, aber ich habe zumindest anekdotisch gehört, dass er auf einigen Compilern funktioniert. Ich kann keinen relevanten Abschnitt in der C ++ - Spezifikation dazu finden, und wenn jemand etwas Bestimmtes zitieren kann, das besagt, dass es legal ist oder nicht, wäre das großartig.


Mitgliedsfunktionen einer verschachtelten Klasse folgen regulären Zugriffsregeln und haben keine besonderen Zugriffsrechte für Mitglieder ihrer einschließenden Klassen: publib.boulder.ibm.com/infocenter/comphelp/v8v101/…
Anycorn

1
@ aaa- Danke für den Link, aber dies scheint nur für den IBM-Compiler zu gelten, von dem ich weiß, dass er einige Freiheiten mit der Spezifikation erfordert (zum Beispiel, damit Sie die Adresse eines Etiketts mit dem &&Operator abrufen können). Es tut mir leid, wenn ich in diesem Fall ein Stickler bin, aber ich unterrichte einen C ++ - Programmierkurs und möchte mir der Antwort sehr sicher sein, bevor ich meinen Schülern etwas erzähle.
Templatetypedef

Ich denke nicht, denn wenn dies der Fall wäre, müssten wir nicht ausdrücklich Freundklassen auch innerhalb des Klassenkörpers deklarieren. In diesem Fall sollte nur eine Erklärung ausreichen
Mukeshkumar

@template AFAIK sie listen speziell nicht standardmäßige Erweiterungen auf.
Anycorn

4
Die Frage ist übrigens nicht "ist es ein Freund", sondern "hat es privaten Zugang". (Ersteres ist ausreichend, aber nicht notwendig.)
GManNickG

Antworten:


76

Nachdem ich hier selbst mehr oder weniger dieselbe Frage gestellt hatte , wollte ich die (anscheinend) aktualisierte Antwort für C ++ 11 teilen:

Zitiert von https://stackoverflow.com/a/14759027/1984137 :

Standard $ 11.7.1

"Eine verschachtelte Klasse ist ein Mitglied und hat als solches die gleichen Zugriffsrechte wie jedes andere Mitglied. Die Mitglieder einer einschließenden Klasse haben keinen besonderen Zugriff auf Mitglieder einer verschachtelten Klasse. Die üblichen Zugriffsregeln sind einzuhalten."

und die üblichen Zugriffsregeln legen Folgendes fest:

"Ein Mitglied einer Klasse kann auch auf alle Namen zugreifen, auf die die Klasse Zugriff hat ..."

Spezifische Beispiele wurden in der Norm angegeben:

class E {
    int x;
    class B { };

    class I {
        B b; // OK: E::I can access E::B
        int y;
        void f(E* p, int i) {
            p->x = i; // OK: E::I can access E::x
        }
    };
}

2
Stimme voll zu. Ich denke, die Antwort auf diese Frage sollte überarbeitet werden, da der in der Frage dargestellte Code jetzt korrekt kompiliert werden kann. Hier ist der Beweis: Link
Givi

gcc 4.8.2 hat einen Fehler, der nur in gcc 4.9.0 behoben wurde. gcc.gnu.org/bugzilla/show_bug.cgi?id=59482
Paulo Neves

Kurzum: Ja für C ++ 11 und höher, aber nein für C ++ 03 und höher.
Daniel Kiss

43

Bis C ++ 11 (dh C ++ 98 und C ++ 03)

In C ++ 98 und C ++ 03, verschachtelte Klasse kann nicht Zugriff privateund protectedMitglieder von Standardklasse umschließt.

Der C ++ Standard (2003) sagt in $ 11.8 / 1 [class.access.nest],

Die Mitglieder einer verschachtelten Klasse haben keinen besonderen Zugang zu Mitgliedern einer einschließenden Klasse oder zu Klassen oder Funktionen, die einer einschließenden Klasse Freundschaft gewährt haben. Die üblichen Zugangsregeln (Ziffer 11) sind einzuhalten. Die Mitglieder einer einschließenden Klasse haben keinen besonderen Zugriff auf Mitglieder einer verschachtelten Klasse. Die üblichen Zugangsregeln (Ziffer 11) sind einzuhalten.

Beispiel aus dem Standard selbst:

class E 
{
    int x;
    class B { };
    class I 
    {
        B b; // error: E::B is private
        int y;
        void f(E* p, int i)
        {
           p->x = i; // error: E::x is private
        }
   };
   int g(I* p)
   {
       return p->y; // error: I::y is private
   }
};

Seit C ++ 11

Die obige Einschränkung wurde seit C ++ 11 entfernt. Nun sind die verschachtelten Klassen können Zugriff auf die privateund die protectedMitglieder der umschließenden Klasse:

class E 
{
    int x;
    class B { };
    class I 
    {
        B b; // ok: even though E::B is private
        int y;
        void f(E* p, int i)
        {
           p->x = i; // ok: even though E::x is private
        }
   };
   int g(I* p)
   {
       return p->y; // ok: even though I::y is private
   }
};

Hoffentlich hilft das.


@templatetypedef: Ich wusste, dass verschachtelte Klassen nicht auf private Mitglieder der einschließenden Klasse zugreifen können, habe aber die falsche Referenz angegeben. Jedenfalls habe ich die Referenz korrigiert!
Nawaz

2
Nach dem Weglassen g()wird der Code ab C ++ 11 problemlos kompiliert . Sollte die Antwort aktualisieren.
Iammilind

Es wäre großartig, wenn Sie die Antwort mit relevanten Informationen über C ++ 11 aktualisieren würden, da es einen wesentlichen Unterschied gibt;)
Alexey

@ Alexander: Aktualisiert.
Nawaz

1
Ich habe versucht, diesen Code unter xcode 9.2 mit seinem Standard-Compiler (LLVM 9.0) zu kompilieren, aber er wurde nicht kompiliert. Es schlägt bei der Zeilenrückgabe p-> y fehl . Es scheint, dass der Zugriff auf die privaten Mitglieder der Elternklasse in Ordnung ist, aber der Zugriff auf die privaten Mitglieder der Kinderklasse aus der Elternklasse nicht in Ordnung ist.
Rafael Sabino

16

Da der Fragesteller eine der Antworten akzeptiert zu haben scheint, ist dies nur eine Ergänzung.
Der Standard scheint die Spezifikation bezüglich der Zugänglichkeit geändert zu haben.

§11.8 / 1 in C ++ 98 besagt:

Die Mitglieder einer verschachtelten Klasse haben keinen besonderen Zugang zu Mitgliedern einer einschließenden Klasse oder zu Klassen oder Funktionen, die einer einschließenden Klasse Freundschaft gewährt haben. Die üblichen Zugangsregeln sind einzuhalten.

§ 11.8 / 1 in N1804 (nach TR1) besagt:

Eine verschachtelte Klasse ist ein Mitglied und hat als solches die gleichen Zugriffsrechte wie jedes andere Mitglied.

Ich denke, aktuelle C ++ - Compiler befolgen neuere Spezifikationen.


2
Eine verschachtelte Klasse ist ein Mitglied, aber können verschachtelte Klassenmitglieder als Mitglieder der einschließenden Klasse betrachtet werden? Es ist nicht offensichtlich. Die verschachtelte Klasse selbst (nicht ihre Mitglieder) kann wie folgt auf Mitglieder der einschließenden Klasse zugreifen : class Enclosing {private: enum PrivateEnum {VALUE}; class Nested {/*accessing enclosing class' member type*/ PrivateEnum e;}; };.
Sergei Tachenov

1
@ SergeyTachenov: Hallo. In § 11.8 / 1 in N1804 heißt es außerdem: Die Mitglieder einer einschließenden Klasse haben keinen besonderen Zugang zu Mitgliedern einer verschachtelten Klasse ; Die üblichen Zugangsregeln sind einzuhalten. Diese Anweisung hat sich gegenüber C ++ 98 nicht geändert. Für den Zugriff von der einschließenden Klasse auf die verschachtelte Klasse (das Gegenteil der Frage von templatetypedef) gelten die üblichen Zugriffsregeln.
Ise Wisteria

@Ise, ich spreche nicht über den Zugriff auf Mitglieder einer verschachtelten Klasse, es ist ein ganz anderes Problem. Ich bin mir nur nicht sicher, ob das Konzept "Eine verschachtelte Klasse ist ein Mitglied" auch für die Mitglieder der verschachtelten Klasse gilt. Was ist dann mit mehreren Verschachtelungsebenen?
Sergei Tachenov

@ SergeyTachenov: Entschuldigung, ich habe Ihren Kommentar falsch verstanden. Wenn Sie meinen, ob class A { int i; class B { struct C { void f( A* x ) { x->i = 0; } }; }; };der Standard zulässig ist, ist es vermutlich die Absicht des Standards, dies zuzulassen. VC8, g ++ 3.4.5 und Comeau Online erlauben es. Aber ich kann nichts Bestimmtes sagen. Wenn Sie besorgt sind, würde ich empfehlen, diese Frage in StackOverflow zu posten. Jemand mit detaillierteren Kenntnissen als ich wird antworten.
Ise Wisteria

@Ise, tut mir leid, dass ich irrelevante Dinge eingefügt habe. Anfangs habe ich das Zitat aus N1804 kommentiert: "Eine verschachtelte Klasse ist ein Mitglied" - bedeutet dies, dass Mitglieder der verschachtelten Klasse auch Mitglieder der einschließenden Klasse sind? Ich meine, in class A {int i; class B {A *a; int f() {return a->i;} }; };der Klasse B ist ein Mitglied der Klasse A, aber B :: f () ist ein Mitglied der Klasse A :: B, nicht A! Aber ich habe gerade ein anderes Zitat aus DR 45 gefunden, das dies klarstellen könnte: "Ein Mitglied einer Klasse kann auch auf alle Namen als die Klasse zugreifen, zu der es gehört." Mit anderen Worten, B :: f () "erbt" das Zugriffsrecht der Klasse B.
Sergei Tachenov

4

Diese Antwort bezieht sich auf die (veraltete) C ++ 03-Spezifikation. Die akzeptierte Antwort auf diese Frage ist aktueller.

Nun, ich finde es albern, diese Frage jetzt zu stellen, weil ich gerade den relevanten Teil der Spezifikation gefunden habe, der dies abdeckt: §11.8 / 1:

Die Mitglieder einer verschachtelten Klasse haben keinen besonderen Zugang zu Mitgliedern einer einschließenden Klasse oder zu Klassen oder Funktionen, die einer einschließenden Klasse Freundschaft gewährt haben. Die üblichen Zugangsregeln (Ziffer 11) sind einzuhalten. Die Mitglieder einer einschließenden Klasse haben keinen besonderen Zugriff auf Mitglieder einer verschachtelten Klasse. Die üblichen Zugangsregeln (Ziffer 11) sind einzuhalten

(Meine Betonung)

Es sieht also so aus, als ob innere Klassen keine speziellen Zugriffsrechte haben.


Sie verstehen diesen Absatz falsch. Es gibt kein 11.8.1, meinten Sie 11.8p1 (oder "11.8 / 1")?
Fred Nurk

@ Fred Nurk-Whoops, bedeutete 11,8 / 1. Wird behoben.
Templatetypedef

@Fred Nurk- Wenn Sie sagen, dass ich es falsch verstehe, bezog sich das dann darauf, dass ich es falsch interpretierte oder es nur falsch beschriftete?
Templatetypedef

und @Fred: templatetypedef ist korrekt. Bitte siehe meinen Beitrag. Ich habe auch das Beispiel von Standard selbst zitiert.
Nawaz

3

Ich weiß nicht genau, wo ich mich befinde, aber ich erinnere mich, dass ich die Spezifikationen durchgelesen und festgestellt habe, dass alle privaten Daten in einer Klasse vor allen anderen Klassen, einschließlich verschachtelter Klassen, verborgen sind.

Grundsätzlich definiert das Verschachteln einer Klasse einen bestimmten Bereich und nicht den Zugriff auf Berechtigungen.


1
Sie sind falsch, aber ich bin nur -1'ing, weil irgendwie, selbst mit der zitierten richtigen Antwort in der Nähe, dies + 1'd bekam. @Voters, bitte lesen Sie alle Antworten, bevor Sie wohl oder übel abstimmen.
GManNickG
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.