Могут ли внутренние классы получить доступ к частным переменным?

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

Эта ошибка возникает, когда я компилирую с классом Outer :: Inner 'не имеет члена с именем `var'

кал
источник

Ответы:

120

Внутренний класс является другом класса, в котором он определен.
Так да; объект типа Outer::Innerможет получить доступ к переменной-члену varобъекта типаOuter .

Однако, в отличие от Java, нет никакой корреляции между объектом типа Outer::Innerи объектом родительского класса. Вы должны вручную установить отношения родитель-ребенок.

#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
Технически в текущем стандарте C ++ вложенный класс НЕ имеет специального доступа к своему классу включения. См. Раздел 11.8.1 стандарта. ОДНАКО см. Также этот стандартный дефект: open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#45
Грег Роджерс,
1
Как бы то ни было, GCC следует предлагаемому разрешению, приведенному там, другие компиляторы, вероятно, тоже.
Грег Роджерс,
24
Стандарт C + 11 теперь соответствует приведенному выше описанию.
Мартин Йорк
1
В Java нестатическому внутреннему классу неявно дается ссылка (указатель) на экземпляр его внешнего класса при первом обращении к внутреннему классу. Перефразируя это, jvm неявно пишет для вас код, похожий на тот, который @LokiAstari показал нам в своем ответе. Вот отрывок из 2-го издания Effective Java, «Правило 22: отдавайте предпочтение статическим классам-членам над нестатическими»: «Если вы опустите этот модификатор (ключевое слово static при объявлении внутреннего класса), каждый экземпляр будет иметь постороннюю ссылку на его включающий экземпляр».
Дэвид Ли
3
@Loki Astari: Я прочитал последнее предложение «Вы должны установить отношения между родительскими и дочерними объектами вручную» и интерпретировал последующий фрагмент кода как пример того, как это сделать правильно !
Brent Baccala
32

Внутренний класс имеет доступ ко всем членам внешнего класса, но не имеет неявной ссылки на экземпляр родительского класса (в отличие от некоторых странностей с Java). Итак, если вы передадите ссылку на внешний класс внутреннему классу, он может ссылаться на что угодно во внешнем экземпляре класса.

MSN
источник
7
это верно из c ++ 11
thrantir 07
6

Все, что является частью Outer, должно иметь доступ для всех участников Outer, публичных или частных.

Изменить: ваш компилятор правильный, var не является членом Inner. Но если у вас есть ссылка или указатель на экземпляр Outer, он может получить к нему доступ.

Марк Рэнсом
источник
2

var не является членом внутреннего класса.

Для доступа к var следует использовать указатель или ссылку на экземпляр внешнего класса. например, pOuter-> var будет работать, если внутренний класс является другом внешнего, или var является общедоступным, если он строго следует стандарту C ++.

Некоторые компиляторы относятся к внутренним классам как к друзьям внешних, а некоторые - нет. См. Этот документ для компилятора IBM :

"Вложенный класс объявляется в рамках другого класса. Имя вложенного класса является локальным для его включающего класса. Если вы не используете явные указатели, ссылки или имена объектов, объявления во вложенном классе могут использовать только видимые конструкции, включая имена типов, статические члены и перечислители из включающего класса и глобальных переменных.

Функции-члены вложенного класса следуют обычным правилам доступа и не имеют специальных прав доступа к членам их включающих классов. Функции-члены включающего класса не имеют специального доступа к членам вложенного класса ".

xiaochuanQ
источник
4
Неправильно. Смотрите другие ответы - 3 года назад. «если строго следовать стандарту C ++», они получат разные ответы от ваших. В раннем черновике C ++ 11 вложенные классы могут получать доступ ко всем членам родительского элемента через ссылку / указатель. Не требуется явно указывать friendили public. Кого волнует, что IBM ошибалась / устарела в прошлом при неработающей ссылке? Этот ответ уже устарел за 3 года до публикации.
underscore_d
1

Прежде всего, вы пытаетесь получить доступ к нестатическому члену varвне класса, что недопустимо в C ++.

Ответ Марка правильный.

Все, что является частью Outer, должно иметь доступ для всех участников Outer, публичных или частных.

Таким образом, вы можете сделать две вещи: либо объявить varкак, staticлибо использовать ссылку на экземпляр внешнего класса для доступа к 'var' (поскольку дружественный класс или функция также нуждаются в ссылке для доступа к частным данным).

Статический var

Измените varна, staticесли вы не хотите, varчтобы вас связывали с экземплярами класса.

#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: 1

Нестатическая переменная

Ссылка на объект должна обращаться к любым нестатическим переменным-членам.

#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: 1

Изменить - внешние ссылки - это ссылки на мой блог.

Адарш Кумар
источник