As classes internas podem acessar variáveis ​​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 erro ocorre quando eu compilo com a classe Outer :: Inner 'não tem nenhum membro chamado' var '

Kal
fonte

Respostas:

120

Uma classe interna é amiga da classe dentro da qual é definida.
Então sim; um objeto do tipo Outer::Innerpode acessar a variável varde membro de um objeto do tipo Outer.

Porém, ao contrário do Java, não há correlação entre um objeto do tipo Outer::Innere um objeto da classe pai. Você tem que fazer o relacionamento pai-filho 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
fonte
14
Tecnicamente, no padrão C ++ atual, uma classe aninhada NÃO tem acesso especial à sua classe envolvente. Consulte a seção 11.8.1 do padrão. NO ENTANTO, veja também este defeito padrão: open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#45
Greg Rogers,
1
Pelo que vale a pena, o GCC segue a resolução proposta dada lá, outros compiladores provavelmente o fazem também.
Greg Rogers,
24
O padrão C + 11 agora é compatível com a descrição acima.
Martin York
1
Em Java, a classe interna não estática recebe implicitamente uma referência (ponteiro) para a instância de sua classe externa quando a classe interna é acessada pela primeira vez. Para reformular isso, jvm está implicitamente escrevendo um código para você que é semelhante ao que @LokiAstari nos mostrou em sua resposta. Aqui está um trecho de Effective Java 2ª Ed "Item 22: Favorecer classes de membros estáticos em vez de não estáticos": "Se você omitir este modificador (palavra-chave estática ao declarar a classe interna), cada instância terá uma referência estranha à sua instância envolvente".
David Lee
3
@Loki Astari: Eu li a última frase "Você tem que fazer o relacionamento pai-filho manualmente" e interpretei o fragmento de código que se seguiu como um exemplo de como fazer isso corretamente !
Brent Baccala de
32

Uma classe interna tem acesso a todos os membros da classe externa, mas não tem uma referência implícita a uma instância de classe pai (ao contrário de algumas estranhezas com Java). Portanto, se você passar uma referência à classe externa para a classe interna, ela poderá fazer referência a qualquer coisa na instância da classe externa.

MSN
fonte
7
isso é verdade a partir de c ++ 11
thrantir
6

Qualquer coisa que faça parte da Outer deve ter acesso a todos os membros da Outer, públicos ou privados.

Editar: seu compilador está correto, var não é membro do Inner. Mas se você tiver uma referência ou ponteiro para uma instância de Outer, ele poderá acessá-la.

Mark Ransom
fonte
2

var não é membro da classe interna.

Para acessar var, um ponteiro ou referência a uma instância de classe externa deve ser usado. por exemplo, pOuter-> var funcionará se a classe interna for amiga da externa, ou var for pública, se seguirmos estritamente o padrão C ++.

Alguns compiladores tratam as classes internas como amigas das externas, mas outros não. Consulte este documento para compilador IBM :

"Uma classe aninhada é declarada dentro do escopo de outra classe. O nome de uma classe aninhada é local para sua classe envolvente. A menos que você use ponteiros explícitos, referências ou nomes de objeto, as declarações em uma classe aninhada só podem usar construções visíveis, incluindo nomes de tipo, membros estáticos e enumeradores da classe envolvente e variáveis ​​globais.

As funções de membro de uma classe aninhada seguem regras de acesso regulares e não têm privilégios de acesso especiais para membros de suas classes incluídas. As funções de membro da classe envolvente não têm acesso especial aos membros de uma classe aninhada. "

xiaochuanQ
fonte
4
Errado. Veja outras respostas - 3 anos antes. "se alguém segue o padrão C ++ estritamente", eles chegam a respostas diferentes das suas. Em um rascunho inicial do C ++ 11, as classes aninhadas podem acessar todos os membros do pai por meio de uma referência / ponteiro. Não há nenhum requisito para declarar explicitamente friendou public. Quem se importa se a IBM estava errada / desatualizada, no passado, em um link morto? Esta resposta já estava desatualizada 3 anos antes de ser postada.
underscore_d
1

Em primeiro lugar, você está tentando acessar um membro não estático varfora da classe, o que não é permitido em C ++.

A resposta de Mark está correta.

Qualquer coisa que faça parte da Outer deve ter acesso a todos os membros da Outer, públicos ou privados.

Portanto, você pode fazer duas coisas, declarar varcomo staticou usar uma referência de uma instância da classe externa para acessar 'var' (porque uma classe ou função amiga também precisa de referência para acessar dados privados).

Var estática

Mude varpara staticSe você não quiser varser associado às instâncias da 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();

}

Saída- var: 1

Var não estático

A referência de um objeto deve acessar quaisquer variáveis ​​de membro não estáticas.

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

}

Saída- var: 1

Editar - Links externos são links para meu blog.

Adarsh ​​Kumar
fonte