2012-04-16 6 views
6

Refiriéndose otra so questionconfusión en cuanto a nombre de su escondite y funciones virtuales

Considere el código:

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(){ 

    // 1)  
    Derived * obj = new Derived ; 
    obj->gogo(7); // this is illegal because of name hiding 


    // 2)  
    Base* obj = new Derived ; 
    obj->gogo(7); // this is legal 
} 

Para el caso 2)

La llamada obj->gogo(7) se resuelve en tiempo de ejecución.

Desde obj->gogo(7) es legal. Parece implicar que vtable de Derived contiene ptr a virtual void gogo(int a) que debería haberse ocultado.

Mi confusión es, puesto que el nombre escondite hace caso 1) que es ilegal, entonces, ¿cómo la llamada en 2) se resuelve en tiempo de ejecución

a) ¿vtable del Derivado contiene puntero a gogo (int).

b) Si a) no es Verdadero, ¿la resolución de llamada para funciones virtuales pasa a vtable de clase base.

+0

@AndersK La función 'Base :: gogo (int)' está de hecho oculta por 'Derived :: gogo (int *)'. Pero una instrucción 'using Base :: gogo;' en la clase 'Derived' resolvería este problema en particular. –

+0

@MichaelWild sí, vi mi error. –

Respuesta

0

Desde que declaró el segundo obj como Base*, el vtable le da todos los métodos de Base. Aunque para los métodos virtuales que han sido reemplazados por Derived, se invocan las versiones sustituidas, los demás métodos (o sobrecargas de métodos) siguen siendo los que se han declarado en Base.

Sin embargo, si usted declaró el puntero como Derived*, la viable le dará los métodos de Derived, ocultando las que tenían el mismo nombre en Base. Por lo tanto, obj->gogo(7); no funcionará. Del mismo modo, esto también es ilegal:

Base* obj = new Derived(); 

// legal, since obj is a pointer to Base, it contains the gogo(int) method. 
obj->gogo(7); 

// illegal, it has been cast into a pointer to Derived. gogo(int) is hidden. 
(reinterpret_cast<Derived*>(obj))->gogo(7); 

Esto es legal:

Derived* obj = new Derived ; 
obj->Base::gogo(7); // legal. 

Ver here.

+0

¿No sería 'dynamic_cast' más apropiado en su ejemplo? – Griwes

+0

Hasta donde yo sé, esto no tiene nada que ver con la búsqueda real de 'vtable', pero con el tipado estático y el ocultamiento de nombres (ver la respuesta de Bo Persson). Si type es 'Derived', se aplica esa semántica, si type es' Base', se aplica otra semántica. – KillianDS

5

Está confundiendo las llamadas a funciones virtuales y la resolución de sobrecarga.

Todas las clases derivadas tienen tablas virtuales que contienen todas las funciones virtuales, desde la clase base y cualquier función virtual propia adicional. Esto se usa para resolver llamadas al runtime, como en su caso 2).

En el caso 1) se obtiene un error de resolución de sobrecarga en compilación tiempo. Debido a la ocultación de nombres, la clase Derived solo tiene una función invocable. Su única opción es llamar a esa función, con un int*.

+0

No, no hay ningún error. Dado que ambas sobrecargas 'gogo()' se declararon en 'Base', anular una no ocultará la otra. –

+0

esto explica el comportamiento anterior. ¿C++ estándar (o cualquier libro/documento) describe lo mismo. Quiero decir que es posible obtener una cita. – fizzbuzz

+0

@fizzbuzz: un nombre declarado en un ámbito interno siempre oculta un nombre en un ámbito externo. En este caso, la clase derivada es el alcance interno y la clase base el ámbito externo. –

1

Básicamente, la sobrecarga de funciones ocurre solo cuando las funciones del mismo nombre están definidas en el mismo ámbito. Ahora, la clase Base tiene su propio alcance y la clase derivada tiene la suya propia.

Por lo tanto, cuando no redefine una función en la clase derivada y llama a esa función, el compilador verifica el alcance del derivado, descubre que no hay tal función definida allí. Luego, verifica el alcance de la clase base, descubre la función y, por lo tanto, vincula la llamada a la función a esta definición particular.

Pero, cuando redefine la función con una firma diferente, el compilador hace coincidir la llamada con esta función, ve la incoherencia y simplemente se queja.

puede cambiar este comportamiento agregando "using Base :: gogo;" en la defensa de clase derivada. Espero que esto explique.

2

Quiere anular la función sobrecargada pero las reglas de ocultación no funcionan así.

"La regla de ocultación dice que una entidad en un ámbito interno oculta cosas con el mismo nombre en un ámbito externo".

Nota, es irrelevante que tenga una firma diferente, es decir, gogo(int* a) ocultará todas las funciones gogo(whatever) de la Base. La sobrescritura ocurre solo cuando sus funciones tienen el mismo nombre, mismas firmas y virtuales (por lo tanto, solo se sobrescribirá gogo(int* a)).

El libro de preguntas frecuentes de C++ sugiere utilizar "sobrecargas no virtuales que invocan a los virtuales no sobrecargados". (capítulo 29.05). Básicamente se crea no virtual funciones sobrecargadas en la clase base:

gogo(int a) and gogo(int* a)

que llamará funciones virtuales, respectivamente:

gogo_i virtual (int a) y gogo_pi virtual (int * a)

Y anule este virtual en Clase derivada.

Cuestiones relacionadas