Le classi interne possono accedere a variabili private?


117
class Outer {

    class Inner {
    public:
        Inner() {}
        void func() ;
    };

private:
    static const char* const MYCONST;
    int var;
};

void Outer::Inner::func() {
    var = 1;
}

const char* const Outer::MYCONST = "myconst";

Questo errore si verifica quando compilo con la classe Outer :: Inner 'non ha un membro chiamato `var'

Risposte:


120

Una classe interna è un amico della classe in cui è definita.
Quindi sì; un oggetto di tipo Outer::Innerpuò accedere alla variabile membro vardi un oggetto di tipo Outer.

A differenza di Java, tuttavia, non esiste alcuna correlazione tra un oggetto di tipo Outer::Innere un oggetto della classe genitore. Devi creare manualmente la relazione genitore-figlio.

#include <string>
#include <iostream>

class Outer
{
    class Inner
    {
        public:
            Inner(Outer& x): parent(x) {}
            void func()
            {
                std::string a = "myconst1";
                std::cout << parent.var << std::endl;

                if (a == MYCONST)
                {   std::cout << "string same" << std::endl;
                }
                else
                {   std::cout << "string not same" << std::endl;
                }
            }
        private:
            Outer&  parent;
    };

    public:
        Outer()
            :i(*this)
            ,var(4)
        {}
        Outer(Outer& other)
            :i(other)
            ,var(22)
        {}
        void func()
        {
            i.func();
        }
    private:
        static const char* const MYCONST;
        Inner i;
        int var;
};

const char* const Outer::MYCONST = "myconst";

int main()
{

    Outer           o1;
    Outer           o2(o1);
    o1.func();
    o2.func();
}

14
Tecnicamente nell'attuale standard C ++, una classe annidata NON ha un accesso speciale alla sua classe che la racchiude. Vedi sez 11.8.1 della norma. TUTTAVIA vedere anche questo difetto standard: open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#45
Greg Rogers

1
Per quel che vale, GCC segue la proposta di risoluzione fornita lì, probabilmente anche altri compilatori.
Greg Rogers,

24
Lo standard C + 11 è ora conforme alla descrizione di cui sopra.
Martin York

1
In Java, alla classe interna non statica viene implicitamente dato un riferimento (puntatore) all'istanza della sua classe esterna quando si accede per la prima volta alla classe interna. Per riformulare questo, jvm sta scrivendo implicitamente un codice per te che è simile a quello che @LokiAstari ci ha mostrato nella sua risposta. Ecco un estratto da Effective Java 2nd Ed "Item 22: Favor static member classes over nonstatic": "Se ometti questo modificatore (parola chiave statica quando dichiari la classe interna), ogni istanza avrà un riferimento estraneo alla sua istanza che lo racchiude".
David Lee

3
@Loki Astari: ho letto l'ultima frase "Devi creare manualmente la relazione genitore-figlio" e ho interpretato il frammento di codice che segue come un esempio di come farlo correttamente !
Brent Baccala

32

Una classe interna ha accesso a tutti i membri della classe esterna, ma non ha un riferimento implicito a un'istanza della classe genitore (a differenza di alcune stranezze con Java). Quindi, se si passa un riferimento alla classe esterna alla classe interna, può fare riferimento a qualsiasi cosa nell'istanza della classe esterna.


7
questo è vero da c ++ 11
thrantir

6

Tutto ciò che fa parte di Outer dovrebbe avere accesso a tutti i membri di Outer, pubblici o privati.

Modifica: il tuo compilatore è corretto, var non è un membro di Inner. Ma se hai un riferimento o un puntatore a un'istanza di Outer, potrebbe accedervi.


2

var non è un membro della classe interna.

Per accedere a var, è necessario utilizzare un puntatore o un riferimento a un'istanza di una classe esterna. pOuter-> var funzionerà se la classe interna è amica di quella esterna, oppure var è pubblica, se si segue rigorosamente lo standard C ++.

Alcuni compilatori considerano le classi interne come amiche di quelle esterne, ma altri no. Vedere questo documento per il compilatore IBM :

"Una classe nidificata viene dichiarata nell'ambito di un'altra classe. Il nome di una classe nidificata è locale rispetto alla classe che la racchiude. A meno che non si utilizzino puntatori, riferimenti o nomi di oggetti espliciti, le dichiarazioni in una classe nidificata possono utilizzare solo costrutti visibili, inclusi digitare nomi, membri statici ed enumeratori dalla classe di inclusione e dalle variabili globali.

Le funzioni membro di una classe annidata seguono regole di accesso regolari e non hanno privilegi di accesso speciali per i membri delle loro classi che la racchiudono. Le funzioni membro della classe che la racchiude non hanno accesso speciale ai membri di una classe nidificata. "


4
Sbagliato. Vedi altre risposte - 3 anni prima. "se si segue rigorosamente lo standard C ++", raggiungono risposte diverse dalle tue. A partire da una prima bozza per C ++ 11, le classi annidate possono accedere a tutti i membri del genitore tramite un riferimento / puntatore. Non è necessario dichiarare esplicitamente friendo public. Chi se ne frega se IBM ha sbagliato / obsoleto, in passato, a un collegamento morto? Questa risposta era già scaduta 3 anni prima che fosse pubblicata.
underscore_d

1

Prima di tutto, stai cercando di accedere a un membro non statico varal di fuori della classe che non è consentito in C ++.

La risposta di Marco è corretta.

Tutto ciò che fa parte di Outer dovrebbe avere accesso a tutti i membri di Outer, pubblici o privati.

Quindi puoi fare due cose, dichiarare varcome statico usare un riferimento di un'istanza della classe esterna per accedere a 'var' (perché anche una classe o una funzione amico necessita di riferimento per accedere a dati privati).

Var. Statica

Cambia varin staticSe non vuoi varessere associato alle istanze della classe.

#include <iostream>

class Outer {

private:
    static const char* const MYCONST;
    static int var;

public:
   class Inner {
    public:
        Inner() {
          Outer::var = 1;
        }
        void func() ;
    };
};

int Outer::var = 0;

void Outer::Inner::func() {
    std::cout << "var: "<< Outer::var;
}

int main() {
  Outer outer;
  Outer::Inner inner;
  inner.func();

}

Var. Uscita: 1

Var. Non statica

Il riferimento di un oggetto deve accedere a qualsiasi variabile membro non statica.

#include <iostream>

class Outer {

private:
    static const char* const MYCONST;
    int var;

public:
   class Inner {
    public:
        Inner(Outer &outer) {
          outer.var = 1;
        }
        void func(const Outer &outer) ;
    };
};

void Outer::Inner::func(const Outer &outer) {
    std::cout << "var: "<< outer.var;
}

int main() {
  Outer outer;
  Outer::Inner inner(outer);
  inner.func(outer);

}

Var. Uscita: 1

Modifica - I collegamenti esterni sono collegamenti al mio blog.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.