Por que o membro da Superclass Protegido não pode ser acessado em uma função de subclasse quando aprovado como argumento?
-
23-09-2019 - |
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?
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 to
pú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 A
Controle 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 B
Caso.
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;
}