Por que o membro da Superclass Protegido não pode ser acessado em uma função de subclasse quando aprovado como argumento?

StackOverflow https://stackoverflow.com/questions/2557950

Pergunta

Recebo um erro de compilação, sobre o qual estou um pouco confuso. Isso está no VS2003.

Erro C2248: 'A :: y': Não é possível acessar o membro protegido declarado na classe 'A'

class A
{
public:
  A() : x(0), y(0) {}
protected:
  int x;
  int y;
};

class B : public A
{
public:
  B() : A(), z(0) {}
  B(const A& item) : A(), z(1) { x = item.y;}
private:
  int z;
};

O problema está com x = item.y;

O acesso é especificado como protegido. Por que o construtor da classe B não tem acesso a a :: y?

Foi útil?

Solução

É por causa disso:

class base_class
{
protected:
    virtual void foo() { std::cout << "base::foo()" << std::endl; }
};

class A : public base_class
{
protected:
    virtual void foo() { std::cout << "A::foo()" << std::endl; }
};

class B : public base_class
{
protected:
    virtual void foo() { std::cout << "B::foo()" << std::endl; }

public:
    void bar(base_class *b) { b->foo(); }
};

Se isso fosse legal, você poderia fazer isso:

A a;
B b;
b.bar(&a);

E você estaria ligando para um protected membro de A de B, que não é permitido.

Outras dicas

As outras respostas explicam o raciocínio por trás da prevenção do seu B objeto de acessar as partes protegidas de A no seu exemplo, embora B 'é um' A. Obviamente, a maneira mais fácil de resolver esse problema é fazer as partes de A you want access topúblico 'ou possui métodos de acessórios acessíveis ao público.

No entanto, você pode decidir que é inapropriado (ou pode não ter controle sobre a definição de A). Aqui estão algumas sugestões para permitir que você contorne o problema, em uma ordem crescente de subverter AControle de Acesso. Observe que todas essas soluções alternativas assumem que class A é construível com cópia.

No primeiro caso, você simplesmente usa o construtor de cópias para A para configurar um estado inicial para essa parte do B Objeto e, em seguida, conserte depois:

class B1 : public A
{
public:
  B1() : A(), z(0) {}
  B1(const A& item) : A(item), z(1) {
    // fix up the A sub-object that was copy constructed 
    //  not quite the way we wanted
    x = y;
    y = 0;
  }
private:
  int z;
};

Acho isso incrivelmente confuso e provavelmente muito propenso a erros (assumindo que queremos o A subobjeto no B objeto de ser diferente do A Objeto que está sendo passado para o construtor - uma situação incomum, mas é o que foi dado no problema). No entanto, o fato de que isso pode ser feito dá alguma justificativa para os exemplos mais subversivos que se seguem ...

O próximo exemplo cria um temporário B objeto que tem uma duplicata exata do A objeto que queremos acessar. Podemos então usar o temporário B Objeta -se para chegar aos itens que foram protegidos:

class B2 : public A
{
public:
  B2() : A(), z(0) {}
  B2(const A& item) : A(), z(1) {
    // create a special-use B2  object that can get to the 
    //  parts of the A object we want access to
    B2 tmp( item, internal_use_only);

    x = tmp.y;  // OK since tmp is of type B
  }

private:
  int z;

  // create a type that only B2 can use as a 
  //    'marker' to call a special constructor 
  //    whose only purpose in life is to create
  //    a B object with an exact copy of another
  //    A sub-object in it
  enum internal_use {
    internal_use_only
  };
  B2( const A& item, internal_use marker) : A(item), z(0) {};
};

Acho essa solução um pouco menos confusa que a primeira, mas ainda é confusa (na minha opinião). Ter uma versão bastarda do objeto B apenas para chegar às partes do objeto A que queremos é estranho.

Podemos fazer algo sobre isso criando um proxy especial para A objetos que dão o acesso que queremos. Observe que esta é a solução alternativa 'mais subversiva', porque é algo que qualquer classe poderia fazer para chegar a partes protegidas de A, mesmo que eles não sejam subclasses de A eles mesmos. No caso do B classe, há alguma legitimidade em chegar às partes protegidas de A objetos, desde então B é um A, e como já vimos, existem soluções alternativas que nos permitem obter acesso que usam apenas direitos que class B já tem, então considero isso uma versão mais limpa dessas soluções alternativas em class BCaso.

class B3 : public A
{
public:
  B3() : A(), z(0) {}
  B3(const A& item) : A(), z(1) { 
    // a special proxy for A objects that lets us
    //  get to the parts of A we're interested in
    A_proxy tmp( item);
    x = tmp.get_y();
  }

private:
  int z;

    class A_proxy : public A
    {
    public:
        A_proxy( const A& other) : A(other) {};
        int get_x() {return x;};
        int get_y() {return y;};
    };

};

A documentação da IBM resume melhor:

Um membro da classe base não estática protegido pode ser acessado por membros e amigos de qualquer classes derivados dessa classe base usando um dos seguintes:

  • Um ponteiro para uma classe direta ou indiretamente derivada
  • Uma referência a uma classe direta ou indiretamente derivada
  • Um objeto de uma classe direta ou indiretamente derivada

Assim, usando seu exemplo acima como base:

B::B(const A& item) : A(), z(1) {
  // NOT OK because `item` is not a reference to the derived class B
  //int i = item.y; 

  // OK because `item` reinterpreted as a reference to the derived class B
  // Do not do this (bad!) -- for illustrative purposes only
  int i = reinterpret_cast< const B& >(item).y;

  // OK because it is equivalent to `this->x = i`,
  //  where `this` is a pointer to the derived class B
  x = i;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top