c++ - simple - tipos de herencia en java
Error con la herencia de clase anidada (5)
Cita de cppreference :
Un nombre que es privado de acuerdo con la búsqueda de nombre no calificado, puede ser accesible a través de la búsqueda de nombre calificado
Con esto en mente, veamos cómo funcionaría la búsqueda de nombres no calificados para el primer ejemplo:
class A {};
class B : private A {};
class C : private B
{
public:
class D : private A {}; // Error here
};
-
A
se busca dentro del alcance deC
Si se hubiera definido allí, no habría ningún problema. - Se da cuenta de que
A
es heredado de forma privada por su claseB
(privada) de base y, por lo tanto, arroja un error de compilación. Clang dice:note: constrained by private inheritance here: class B : private A {};
De nuevo, según la cita, debería funcionar, si usa un nombre completamente calificado, como lo ha demostrado
class D : private ::A {};
Y en cuanto a tu último ejemplo:
class A {};
class C : private A
{
public:
class D : private A {};
};
Funciona porque la búsqueda de nombre funciona para todos los nombres que forman parte de la misma clase. Para citar de cppreference nuevamente:
Todos los miembros de una clase (cuerpos de funciones miembro, inicializadores de objetos miembros y todas las definiciones de clase anidadas) tienen acceso a todos los nombres a los que puede acceder una clase.
class A {};
class B : private A {};
class C : private B
{
public:
class D : private A {}; // Error here
};
Este código proporciona el siguiente error (en VS 2013):
nested.cpp (8): error C2247: ''A'' no accesible porque ''B'' usa ''privado'' para heredar de ''A''
Se soluciona si cambio la definición de D
así:
class D : private ::A {};
¿Es este comportamiento correcto, y si es así por qué?
Al principio pensé que era porque C
hereda en privado de B
lo que ocultaría las clases base. Pero si elimino la clase B
"hombre medio" y solo uso esto:
class A {};
class C : private A
{
public:
class D : private A {};
};
El error desaparece
Ejemplo de cppreference :
class A { };
class B : private A { };
class C : public B {
A* p; // error: unqualified name lookup finds A as the private base of B
::A* q; // OK, qualified name lookup finds the namespace-level declaration
};
Con la herencia privada, los miembros públicos y protegidos de la clase base se convierten en miembros privados de la clase derivada.
class B : private A {};
class C : private B
{
public:
class D : private A {}; // Error because all members of A is private to B so what
//would be the use of this private inheritance if you can''t access any of A''s member.
};
Mientras
class D :private ::A {};
funciona porque los miembros de A se toman directamente del espacio de nombres global, lo que permite a D
tener acceso a los miembros protegidos y públicos de A
Es una cuestión de alcances durante la búsqueda de nombres:
Cuando usas
::A
es un nombre totalmente calificado, por lo tanto, te estás refiriendo explícitamente al espacio de nombres global y seleccionandoA
desde allí.Cuando heredas de
A
,C
(déjame decir) veA
y puedes referirte directamente al nombreA
dentro deC
con un nombre no calificado.Cuando hereda de
B
,C
veB
yA
es privado en su alcance. Es privado, pero existe. ComoA
es un nombre no calificado y se busca ante todo en ese ámbito, resulta ser encontrado e inaccesible, por lo tanto, el error.
Una parte crucial para la comprensión, que no se explica explícitamente en las otras respuestas, es la siguiente:
El nombre de una clase se inyecta en el alcance de las clases.
Es decir, si tiene
class A {};
entonces puede referirse a la clase A
no solo por el nombre ::A
sino también por el nombre A::A
Tenga en cuenta que a pesar de describir la misma clase, esos no son del mismo nombre, ya que están en diferentes ámbitos.
Ahora cuando en el ámbito de A
o una clase que se deriva directa o indirectamente de A
, la búsqueda no calificada encontrará A::A
lugar de ::A
(a menos que A::A
esté oculto por otro nombre).
Además, a diferencia de otros lenguajes, C ++ no oculta los nombres privados de los ámbitos donde no puede acceder a ellos, sino que usa los especificadores de acceso solo como permiso para usar el nombre. Además, esos permisos están vinculados al nombre, no a la entidad nombrada (en este caso, la clase).
Entonces en su código, en la búsqueda no calificada de A
el compilador encuentra el nombre C::B::A::A
que oculta el nombre ::A
, y luego verifica el permiso de acceso y encuentra que este nombre es privado en el contexto actual, ya que es un nombre en el ámbito de C::B::A
que no se puede acceder desde C
ya que A
es una clase base privada de B
clase D: privada :: A {}; Cuando hereda de B, C ve B y A es privado en su alcance. Es privado, pero existe. Como A es un nombre no calificado y se busca ante todo en ese ámbito, resulta ser encontrado e inaccesible, por lo tanto, el error.