¿Pueden las clases internas acceder a variables privadas?

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

Este error se produce cuando compilo con la clase Outer :: Inner 'no tiene un miembro llamado' var '

kal
fuente

Respuestas:

120

Una clase interna es amiga de la clase en la que se define.
Entonces sí; un objeto de tipo Outer::Innerpuede acceder a la variable miembro varde un objeto de tipo Outer.

Sin embargo, a diferencia de Java, no existe una correlación entre un objeto de tipo Outer::Innery un objeto de la clase principal. Tienes que hacer la relación padre-hijo manualmente.

#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
fuente
14
Técnicamente, en el estándar actual de C ++, una clase anidada NO tiene acceso especial a su clase adjunta. Consulte la sección 11.8.1 de la norma. SIN EMBARGO, vea también este defecto estándar: open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#45
Greg Rogers
1
Por lo que vale, GCC sigue la resolución propuesta dada allí, otros compiladores probablemente también lo hagan.
Greg Rogers
24
El estándar C + 11 ahora cumple con la descripción anterior.
Martin York
1
En Java, la clase interna no estática recibe implícitamente una referencia (puntero) a la instancia de su clase externa cuando se accede por primera vez a la clase interna. Para reformular esto, jvm está escribiendo implícitamente un código para usted que es similar a lo que @LokiAstari nos mostró en su respuesta. Aquí hay un extracto de Effective Java 2nd Ed "Item 22: Favorecer las clases miembro estáticas sobre las no estáticas": "Si omite este modificador (palabra clave estática al declarar la clase interna), cada instancia tendrá una referencia externa a su instancia adjunta".
David Lee
3
@Loki Astari: Leí la última oración "Tienes que hacer la relación padre-hijo manualmente" e interpreté el fragmento de código que siguió como un ejemplo de cómo hacerlo correctamente .
Brent Baccala
32

Una clase interna tiene acceso a todos los miembros de la clase externa, pero no tiene una referencia implícita a una instancia de clase padre (a diferencia de algunas rarezas con Java). Entonces, si pasa una referencia a la clase externa a la clase interna, puede hacer referencia a cualquier cosa en la instancia de la clase externa.

MSN
fuente
7
esto es cierto desde c ++ 11
thrantir
6

Todo lo que sea parte de Outer debe tener acceso a todos los miembros de Outer, públicos o privados.

Editar: su compilador es correcto, var no es miembro de Inner. Pero si tiene una referencia o un puntero a una instancia de Outer, podría acceder a eso.

Mark Ransom
fuente
2

var no es miembro de la clase interna.

Para acceder a var, se debe usar un puntero o referencia a una instancia de clase externa. Por ejemplo, pOuter-> var funcionará si la clase interna es amiga de la externa, o var es pública, si uno sigue estrictamente el estándar C ++.

Algunos compiladores tratan a las clases internas como amigas de las externas, pero es posible que otros no. Consulte este documento para el compilador de IBM :

"Una clase anidada se declara dentro del alcance de otra clase. El nombre de una clase anidada es local a su clase adjunta. A menos que use punteros, referencias o nombres de objeto explícitos, las declaraciones en una clase anidada solo pueden usar construcciones visibles, incluyendo nombres de tipo, miembros estáticos y enumeradores de la clase adjunta y variables globales.

Las funciones de los miembros de una clase anidada siguen las reglas de acceso regulares y no tienen privilegios de acceso especiales para los miembros de sus clases adjuntas. Las funciones miembro de la clase adjunta no tienen acceso especial a los miembros de una clase anidada ".

xiaochuanQ
fuente
4
Incorrecto. Vea otras respuestas - 3 años antes. "si uno sigue estrictamente el estándar C ++", obtienen respuestas diferentes a las suyas. A partir de un borrador inicial de C ++ 11, las clases anidadas pueden acceder a todos los miembros del padre a través de una referencia / puntero. No hay ningún requisito para declarar explícitamente friendo public. ¿A quién le importa si IBM estaba equivocado / desactualizado, en el pasado, en un enlace muerto? Esta respuesta ya estaba desactualizada 3 años antes de su publicación.
underscore_d
1

En primer lugar, está intentando acceder a miembros no estáticos varfuera de la clase que no está permitido en C ++.

La respuesta de Mark es correcta.

Todo lo que sea parte de Outer debe tener acceso a todos los miembros de Outer, públicos o privados.

Entonces, puede hacer dos cosas, declarar varcomo statico usar una referencia de una instancia de la clase externa para acceder a 'var' (porque una clase o función amiga también necesita una referencia para acceder a datos privados).

Var estática

Cambie vara staticSi no desea varestar asociado con las instancias de la clase.

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

}

Salida- var: 1

Var no estática

La referencia de un objeto debe acceder a cualquier variable miembro no estática.

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

}

Salida- var: 1

Editar: los enlaces externos son enlaces a mi blog.

Adarsh ​​Kumar
fuente