A juzgar por la redacción de su pregunta (usó la palabra "ocultar"), ya sabe lo que está sucediendo aquí. El fenómeno se llama "ocultación de nombres". Por alguna razón, cada vez que alguien hace una pregunta sobre por qué ocurre la ocultación del nombre, las personas que responden dicen que esto se llama "ocultación del nombre" y explican cómo funciona (lo que probablemente ya sepas), o explican cómo anularlo (lo cual nunca se le preguntó), pero a nadie parece importarle abordar la pregunta real del "por qué".
La decisión, la razón detrás de la ocultación del nombre, es decir, por qué en realidad se diseñó en C ++, es evitar ciertos comportamientos contraintuitivos, imprevistos y potencialmente peligrosos que podrían tener lugar si se permitiera que el conjunto heredado de funciones sobrecargadas se mezclara con el conjunto actual de sobrecargas en la clase dada. Probablemente sepa que en C ++ la resolución de sobrecarga funciona eligiendo la mejor función del conjunto de candidatos. Esto se realiza haciendo coincidir los tipos de argumentos con los tipos de parámetros. Las reglas de coincidencia pueden ser complicadas a veces, y a menudo conducen a resultados que pueden ser percibidos como ilógicos por un usuario no preparado. Agregar nuevas funciones a un conjunto de funciones previamente existentes podría resultar en un cambio bastante drástico en los resultados de resolución de sobrecarga.
Por ejemplo, supongamos que la clase base B
tiene una función miembro foo
que toma un parámetro de tipo void *
y foo(NULL)
se resuelven todas las llamadas a B::foo(void *)
. Digamos que no hay nombre oculto y esto B::foo(void *)
es visible en muchas clases diferentes que descienden B
. Sin embargo, supongamos que en algún descendiente [indirecto, remoto] D
de la clase se define B
una función foo(int)
. Ahora, sin ocultar el nombre D
tiene tanto foo(void *)
y foo(int)
visible y participando en la resolución de sobrecarga. ¿A qué función se foo(NULL)
resolverán las llamadas , si se realizan a través de un objeto de tipo D
? Resolverán D::foo(int)
, desdeint
es una mejor coincidencia para el cero integral (es decirNULL
) que cualquier tipo de puntero. Entonces, a lo largo de la jerarquía, las llamadas sefoo(NULL)
resolver a una función, mientras que en D
(y debajo) de repente se resuelven a otra.
Otro ejemplo se da en The Design and Evolution of C ++ , página 77:
class Base {
int x;
public:
virtual void copy(Base* p) { x = p-> x; }
};
class Derived{
int xx;
public:
virtual void copy(Derived* p) { xx = p->xx; Base::copy(p); }
};
void f(Base a, Derived b)
{
a.copy(&b); // ok: copy Base part of b
b.copy(&a); // error: copy(Base*) is hidden by copy(Derived*)
}
Sin esta regla, el estado de b se actualizaría parcialmente, lo que llevaría a la división.
Este comportamiento se consideró indeseable cuando se diseñó el lenguaje. Como un mejor enfoque, se decidió seguir la especificación de "ocultación del nombre", lo que significa que cada clase comienza con una "hoja limpia" con respecto a cada nombre de método que declara. Para anular este comportamiento, se requiere una acción explícita del usuario: originalmente una redeclaración de los métodos heredados (actualmente en desuso), ahora un uso explícito del uso de la declaración.
Como observó correctamente en su publicación original (me refiero al comentario "No polimórfico"), este comportamiento podría verse como una violación de la relación IS-A entre las clases. Esto es cierto, pero aparentemente en aquel entonces se decidió que, al final, esconderse resultaría ser un mal menor.