sobrecarga sencillos programacion poo polimorfismo orientada objetos metodos herencia ejemplos c++ polymorphism override

c++ - sencillos - sobrecarga de metodos



¿Por qué una función anulada en la clase derivada oculta otras sobrecargas de la clase base? (4)

A juzgar por la redacción de su pregunta (usted usó la palabra "ocultar"), ya sabe lo que está pasando aquí. El fenómeno se llama "ocultación del nombre". Por alguna razón, cada vez que alguien hace una pregunta sobre por qué ocurre la ocultación de nombres, las personas que responden dicen que esto se llama "ocultación de nombres" y explican cómo funciona (lo que probablemente ya conozca), o explican cómo anularlo (que usted anula). nunca se 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é 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 al elegir la mejor función del conjunto de candidatos. Esto se hace haciendo coincidir los tipos de argumentos con los tipos de parámetros. Las reglas de coincidencia podrían ser complicadas a veces y, a menudo, dar lugar a resultados que podrían 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, digamos que la clase base B tiene una función miembro foo que toma un parámetro de tipo void * , y todas las llamadas a foo(NULL) se resuelven en B::foo(void *) . Digamos que no hay ningún nombre oculto y este B::foo(void *) es visible en muchas clases diferentes que descienden de B Sin embargo, digamos que en algún descendiente [indirecto, remoto] D de la clase B se define una función foo(int) . Ahora, sin el nombre oculto, D tiene visibles foo(void *) y foo(int) y participan en la resolución de sobrecarga. ¿Qué función resolverán las llamadas a foo(NULL) si se realizan a través de un objeto de tipo D ? Se resolverán a D::foo(int) , ya que int es una mejor coincidencia para el cero integral (es decir, NULL ) que cualquier tipo de puntero. Entonces, a lo largo de la jerarquía, las llamadas a foo(NULL) resuelven a una función, mientras que en D (y debajo) se resuelven repentinamente a otra.

Otro ejemplo se da en El diseño y la evolución de 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 segmentación.

Este comportamiento se consideró indeseable cuando se diseñó el lenguaje. Como mejor enfoque, se decidió seguir la especificación de "ocultación de 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 por parte del usuario: originalmente una redeclaración de los métodos heredados (actualmente en desuso), ahora un uso explícito de la declaración de uso.

Como observó correctamente en su publicación original (me refiero a la observación "No polimórfica"), 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 el ocultamiento resultaría ser un mal menor.

Considere el código:

#include <stdio.h> class Base { public: virtual void gogo(int a){ printf(" Base :: gogo (int) /n"); }; virtual void gogo(int* a){ printf(" Base :: gogo (int*) /n"); }; }; class Derived : public Base{ public: virtual void gogo(int* a){ printf(" Derived :: gogo (int*) /n"); }; }; int main(){ Derived obj; obj.gogo(7); }

Conseguí este error:

>g++ -pedantic -Os test.cpp -o test test.cpp: In function `int main()'': test.cpp:31: error: no matching function for call to `Derived::gogo(int)'' test.cpp:21: note: candidates are: virtual void Derived::gogo(int*) test.cpp:33:2: warning: no newline at end of file >Exit code: 1

Aquí, la función de la clase Derivada está eclipsando todas las funciones del mismo nombre (no la firma) en la clase base. De alguna manera, este comportamiento de C ++ no se ve bien. No polimorfo.


Esto es "por diseño". En C ++, la resolución de sobrecarga para este tipo de método funciona de la siguiente manera.

  • Comenzando en el tipo de la referencia y luego yendo al tipo base, encuentre el primer tipo que tenga un método llamado "gogo"
  • Considerando solo los métodos llamados "gogo" en ese tipo, encuentre una sobrecarga que coincida

Dado que Derived no tiene una función coincidente llamada "gogo", la resolución de sobrecarga falla.


La ocultación de nombres tiene sentido porque evita las ambigüedades en la resolución de nombres.

Considere este código:

class Base { public: void func (float x) { ... } } class Derived: public Base { public: void func (double x) { ... } } Derived dobj;

Si Base::func(float) no estuviera oculto por Derived::func(double) en Derived, llamaríamos a la función de clase base al llamar a dobj.func(0.f) , aunque un float puede ser promovido a doble .

Referencia: http://bastian.rieck.ru/blog/posts/2016/name_hiding_cxx/


Las reglas de resolución de nombres dicen que la búsqueda de nombres se detiene en el primer ámbito en el que se encuentra un nombre coincidente. En ese punto, las reglas de resolución de sobrecarga se activan para encontrar la mejor coincidencia de funciones disponibles.

En este caso, gogo(int*) se encuentra (solo) en el alcance de la clase Derivada, y como no hay una conversión estándar de int a int *, la búsqueda falla.

La solución es traer las declaraciones de Base a través de una declaración de uso en la clase Derivada:

using Base::gogo;

... permitiría que las reglas de búsqueda de nombres encontraran a todos los candidatos y, por lo tanto, la resolución de sobrecarga continuaría como esperaba.