oriented - C++: razonamiento detrás de la regla de ocultación
encapsulation c# (5)
¿Cuál es el razonamiento detrás de la regla de ocultación en C ++?
class A { void f(int); }
class B : public A { void f(double); } // B::f(int) is hidden
Si es una característica significativa, creo que también debería ser posible ocultar funciones sin definir nuevas funciones con el mismo nombre: algo como esto:
class B : public A { hide void f(double); }
Pero esto no es posible.
No creo que simplifique el trabajo de los compiladores, ya que los compiladores deben poder mostrar las funciones cuando uses explícitamente la directiva
using
:class B : public A { using A::f; void f(double); } // B::f(int) NOT hidden
Entonces, ¿cómo es que hay una regla de ocultamiento?
Hum, las tres respuestas parecen ser buenas y muestran diferentes razones para la regla de ocultación. No estoy seguro de qué respuesta debería aceptar.
Es una pregunta peliaguda, pero aparentemente la idea es que esta característica de ocultación ayuda a evitar errores sutiles cuando se realizan cambios en una clase base (que de otra manera podría "robar" llamadas que antes habrían sido manejadas por la clase derivada). Todavía un cambio en una clase base puede influir en el resultado de la compilación de clases derivadas, así que no creo que entiendo al 100% esta explicación.
Estoy de acuerdo en que este tema se discute con tanta frecuencia que probablemente el ocultamiento en realidad aumenta la cantidad de "sorpresas" en los programadores de C ++.
here puede encontrar una discusión detallada sobre este tema ...
Estoy seguro de que he visto este caso ofrecido por un Bigwig C ++, no estoy seguro de cuál:
struct Base {
void f(const Base&);
};
struct Derived : Base {
using Base::f;
void f(double);
};
int main() {
Derived d;
d.f(''a''); // calls Derived::f
}
Ahora, agregue void f(int);
a Base
, y el significado de los cambios principales: llama a Base::f
porque int
es una mejor coincidencia para char
: es una promoción de enteros en lugar de una conversión estándar.
No está claro si el programador realmente intentará cambiar a la base para captar llamadas con char
, por lo que requerir el using
para ser explícito significa que el comportamiento predeterminado es que el cambio no afecta el código de llamada. Creo que es una llamada marginal, pero creo que el comité decidió que las clases base en C ++ eran lo suficientemente frágiles como son, sin esto también :-)
No hay necesidad de una palabra clave "ocultar" porque no existe un caso comparable para ocultar "f" de la Base cuando no está sobrecargado en Derivado.
Por cierto, he elegido los tipos, y char
es deliberadamente incongruente. Puede obtener casos más sutiles con int
vs unsigned int
lugar de int
vs char
.
No sé cuál es el fundamento original, pero como esconder o no esconder se trata de elecciones igualmente malas wrt. a las funciones, supongo que la razón es tener reglas uniformes : lo mismo que para los nombres definidos en los alcances de llaves rizadas anidadas.
el escondite te ayuda de alguna manera.
agregar un método a una clase base de forma predeterminada no afectará la resolución de sobrecarga para una clase derivada.
y no se topa con la resolución de sobrecarga por un error al dirigir su llamada con un argumento false
, a un método de clase base con un argumento formal void*
. tales cosas.
vítores y hth.,
Otra razón para ocultar la función de miembro de la clase base (con el mismo nombre pero diferentes firmas) puede deberse a la ambigüedad causada por los parámetros opcionales. Considere el siguiente ejemplo:
#include <stdio.h>
class A
{
public:
int foo(int a, int b=0)
{
printf("in A : %d, %d/n", a, b);
}
};
class B : public A
{
public:
int foo(int a)
{
printf("in B : %d/n", a);
foo(a); //B:foo(a) will be called unless we explicitly call A:foo(a)
foo(a, 1); // compile error: no matching function for call to B:foo(int&, int)
}
};
int main()
{
B b;
b.foo(10);
return 0;
}
Si el método foo
en la clase base no se hubiera ocultado, el compilador no podría decidir si se debe llamar a A::foo
o a B::foo
ya que la siguiente línea coincide con ambas firmas:
foo(a);
Probablemente, el motivo sea la especialización de plantilla. Te doy un ejemplo:
template <int D> struct A { void f() };
template <> struct A<1> { void f(int) };
template <int D>
struct B: A<D>
{
void g() { this->f(); }
};
La clase de plantilla B tiene un método f()
, pero hasta que no cree una instancia de la clase B, no conoce la firma. Entonces la llamada this->f()
es en cualquier momento "legal" . Tanto GCC como CLang no informan el error hasta que crea la instancia. Pero cuando llamas al método g()
en una instancia B<1>
, indican el error. Entonces, la regla de ocultamiento es más simple para verificar si su código es válido.
Informo la última parte del código utilizado en mi ejemplo.
int main (int argc, char const *argv[])
{
B<0> b0; /* valid */
B<1> b1; /* valid */
b0.g(); /* valid */
b1.g(); /* error: no matching function for call to ‘B<1>::f()’ */
return 0;
}