Czy klasy wewnętrzne mogą uzyskać dostęp do zmiennych prywatnych?

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";

Ten błąd pojawia się, gdy kompiluję z klasą Outer :: Inner 'nie ma elementu członkowskiego o nazwie `var'

kal
źródło

Odpowiedzi:

120

Klasa wewnętrzna jest przyjacielem klasy, w której jest zdefiniowana.
Więc tak; obiekt typu Outer::Innermoże uzyskać dostęp do zmiennej varskładowej obiektu typu Outer.

Jednak w przeciwieństwie do Javy nie ma korelacji między obiektem typu Outer::Innera obiektem klasy nadrzędnej. Musisz ręcznie utworzyć relację rodzic-dziecko.

#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();
}
Martin York
źródło
14
Z technicznego punktu widzenia w obecnym standardzie C ++ klasa zagnieżdżona NIE ma specjalnego dostępu do klasy otaczającej. Patrz sekcja 11.8.1 normy. JEDNAK zobacz także tę standardową wadę: open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#45
Greg Rogers,
1
Bez względu na to, jaka jest jego wartość, GCC postępuje zgodnie z proponowaną tam rezolucją, prawdopodobnie inne kompilatory też.
Greg Rogers
24
Standard C + 11 jest teraz zgodny z powyższym opisem.
Martin York
1
W języku Java niestatycznej klasie wewnętrznej jest niejawnie przypisywane odwołanie (wskaźnik) do wystąpienia swojej klasy zewnętrznej, gdy po raz pierwszy uzyskuje się dostęp do klasy wewnętrznej. Mówiąc inaczej, jvm pośrednio pisze dla ciebie kod, który jest podobny do tego, co @LokiAstari pokazał nam w swojej odpowiedzi. Oto fragment z Effective Java 2nd Ed "Punkt 22: Preferuj statyczne klasy składowe zamiast niestatycznych": "Jeśli pominiesz ten modyfikator (słowo kluczowe static podczas deklarowania klasy wewnętrznej), każda instancja będzie miała dodatkowe odniesienie do otaczającej ją instancji".
David Lee
3
@Loki Astari: Przeczytałem ostatnie zdanie: „Musisz ręcznie utworzyć relację między rodzicem a dzieckiem” i zinterpretowałem fragment kodu, który nastąpił, jako przykład, jak to zrobić poprawnie !
Brent Baccala
32

Klasa wewnętrzna ma dostęp do wszystkich członków klasy zewnętrznej, ale nie ma niejawnego odwołania do instancji klasy nadrzędnej (w przeciwieństwie do pewnych dziwactw w Javie). Więc jeśli przekażesz referencję do klasy zewnętrznej do klasy wewnętrznej, może ona odwołać się do czegokolwiek w instancji klasy zewnętrznej.

MSN
źródło
7
to prawda od c ++ 11
thrantir
6

Wszystko, co jest częścią Outer, powinno mieć dostęp do wszystkich członków Outer, publicznych lub prywatnych.

Edycja: twój kompilator jest poprawny, var nie jest członkiem Inner. Ale jeśli masz odniesienie lub wskaźnik do wystąpienia Outer, może uzyskać do niego dostęp.

Mark Okup
źródło
2

var nie jest członkiem klasy wewnętrznej.

Aby uzyskać dostęp do zmiennej, należy użyć wskaźnika lub odwołania do instancji klasy zewnętrznej. Np. pOuter-> var zadziała, jeśli klasa wewnętrzna jest przyjacielem zewnętrznej lub var jest publiczna, jeśli ściśle przestrzega się standardu C ++.

Niektóre kompilatory traktują klasy wewnętrzne jak przyjaciela zewnętrznej, ale niektóre nie. Zobacz ten dokument dla kompilatora IBM :

„Klasa zagnieżdżona jest zadeklarowana w zakresie innej klasy. Nazwa klasy zagnieżdżonej jest lokalna dla klasy otaczającej. O ile nie używasz jawnych wskaźników, odwołań lub nazw obiektów, deklaracje w klasie zagnieżdżonej mogą używać tylko widocznych konstrukcji, w tym nazwy typów, statyczne elementy członkowskie i moduły wyliczające z otaczającej klasy i zmiennych globalnych.

Funkcje składowe klasy zagnieżdżonej są zgodne ze zwykłymi regułami dostępu i nie mają specjalnych uprawnień dostępu do członków klas obejmujących. Funkcje składowe klasy otaczającej nie mają specjalnego dostępu do elementów składowych klasy zagnieżdżonej. "

xiaochuanQ
źródło
4
Źle. Zobacz inne odpowiedzi - 3 lata wcześniej. „jeśli ktoś ściśle przestrzega standardu C ++”, uzyskują odpowiedzi inne niż twoje. Począwszy od wczesnej wersji C ++ 11, zagnieżdżone klasy mogą uzyskać dostęp do wszystkich członków rodzica za pośrednictwem odwołania / wskaźnika. Nie ma wymogu jawnego zadeklarowania friendlub public. Kogo obchodzi, czy IBM w przeszłości się mylił / był przestarzały, gdy znajdował się w martwym łączu? Ta odpowiedź była już nieaktualna 3 lata przed opublikowaniem.
underscore_d
1

Przede wszystkim próbujesz uzyskać dostęp do niestatycznego elementu członkowskiego varpoza klasą, co nie jest dozwolone w C ++.

Odpowiedź Marka jest poprawna.

Wszystko, co jest częścią Outer, powinno mieć dostęp do wszystkich członków Outer, publicznych lub prywatnych.

Możesz więc zrobić dwie rzeczy, zadeklarować varjako staticlub użyć odwołania do instancji klasy zewnętrznej, aby uzyskać dostęp do zmiennej (ponieważ zaprzyjaźniona klasa lub funkcja również potrzebuje odwołania, aby uzyskać dostęp do prywatnych danych).

Static var

Zmień varna staticJeśli nie chcesz varbyć powiązany z wystąpieniami klasy.

#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();

}

Wyjście - var: 1

Niestat. Var

Odwołanie do obiektu musi mieć dostęp do wszelkich niestatycznych zmiennych składowych.

#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);

}

Wyjście - var: 1

Edytuj - linki zewnętrzne to linki do mojego bloga.

Adarsh ​​Kumar
źródło