Подтвердить что ты не робот

Управление доступом вложенного класса в С++

Может ли внутренний класс получить доступ к частной переменной-члену своего охватывающего класса? Кажется, что есть некоторые противоречия в Интернете и компиляторе. Компиляция (g++ на cygwin) позволяет это. Но некоторые технические документы говорят, что это запрещено.

4b9b3361

Ответ 1

Эта проблема была рассмотрена в Отчет об ошибках # 10 и Отчет о дефекте № 45.

Оригинальный и текущий стандарт языка С++ не предоставляет вложенным классом дополнительных прав доступа, то есть он не имеет привилегий при доступе к членам охватывающего класса. Вложенные классы - это просто обычные полностью независимые классы, которые просто объявляются внутри какого-либо другого класса.

Но в соответствии с предлагаемой резолюцией в отчете о дефекте № 45 вложенному классу должны быть предоставлены права доступа всем членам охватывающего класса. То есть вложенный класс должен сам считаться членом охватывающего класса и должен пользоваться всеми правами доступа, обычно предоставляемыми членам.

В прошлый раз, когда я проверил, работа над этим дефектом еще не завершена. Некоторые компиляторы уже реализуют то, что, по их мнению, потребует новая спецификация, то есть права на полный доступ к вложенным классам (см. Предлагаемое решение для DR # 45). Некоторые компиляторы придерживаются буквы текущего стандарта. Вот почему вы все еще можете наблюдать некоторую несогласованность между различными компиляторами в отношении прав доступа, предоставляемых вложенным классам.

Ответ 2

Вложенному классу не предоставляется специальный доступ к внешнему классу.

Раздел 11.8-1 стандарта С++ 03 для вложенных классов:

Члены вложенного класса имеют нет специального доступа к членам охватывающий класс, а также классы или функции, которые дали дружбу к охватывающему классу;

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

Ответ 3

В компиляторах С++ 03 это обычно разрешено, потому что комитет понял, что было бы логично разрешить это, потому что вложенные классы являются членами их охватывающего класса. Поэтому они отредактировали стандарт, чтобы разрешить его для С++ 0x (ну, исправление было выполнено в 2001 году), и эти компиляторы реализуют это редактирование задним числом как часть их реализации С++ 03.

Я пробовал GCC, Comeau и Clang, все из которых позволяют это. Точно по правилам С++ 03 это не допускается, хотя никакой компилятор там строго соблюдает закон.


Ловушки

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

class Outer {
  friend class Inner; // wrong, refers to ::Inner
  class Inner { /* ... */ };
};

class Inner { };

Вам нужно поменять порядок, чтобы сделать это правильно

class Outer {
  class Inner { /* ... */ };
  friend class Inner; // correct, refers to Outer::Inner
};

Я видел пару кода, которые сделали это неправильно, но это не было замечено, потому что вложенный класс имел доступ в любом случае.

Другая ошибка заключается в том, что не все части Inner имеют полный доступ к Outer выше. Только те части, где Outer считается полностью определенным классом, имеют такой доступ

class Outer {
  typedef int type; // private type member

  class Inner { 
    type member; // ill-formed: no access
    void f() {
      type var; // OK: access
    }
  };

  friend class Inner; // correct, refers to Outer::Inner
};

Чтобы определить member, вам нужно сначала объявить его, а затем определить

class Outer {
  typedef int type; // private type member

  class Inner; // forward declaration of Outer::Inner
  friend class Inner; // correct, refers to Outer::Inner

  class Inner { 
    type member; // OK: access
    void f() {
      type var; // OK: access
    }
  };
};

Чтобы проверить код друзей, вам нужен старый соответствующий компилятор. Comau Online Compiler в версии 4.3.1 BETA 3/1/03 или ниже.

Ответ 4

"Внутренний" (фактически, только вложенный!) класс в С++, совершенно отличный от Java, не имеет особого статуса. Конечно, все, что нужно сделать внешнему классу, это объявить, что вложенный класс a friend, а затем (как и любой другой класс друзей!) Он получает доступ к частным членам внешнего класса.