2010-01-23 11 views
11

Cuando leo C++ eficaz, dice, nunca redefinir una función no virtual en C++.redefinir una función no virtual en C++

Sin embargo, cuando lo probé, el siguiente código se compila correctamente. ¿Entonces cuál es el punto? ¿Es un error o simplemente una mala práctica?

class A { 

    public: 
    void f() { cout<<"a.f()"<<endl;}; 
}; 

class B: public A { 
    public: 
    void f() { cout<<"b.f()"<<endl;}; 
}; 


int main(){ 

    B *b = new B(); 
    b->f(); 
    return 0; 
} 

Respuesta

24

La redefinición de una función no virtual está bien siempre que no dependa del comportamiento del envío virtual.

El autor del libro es miedo de que pasar su B* a una función que toma un A* y luego se molesta cuando el resultado es una llamada al método de base, no el método derivado.

+2

Lovely answer! Breve, al grano, y muestra cómo es la programación emocional. – DarenW

+0

No estoy de acuerdo, el autor Scott Meyers señala que la herencia pública establece un invariante sobre la especialización para la clase b. Además, el uso de clases es confuso cuando el comportamiento del comportamiento f() depende de la definición del puntero y no de la definición del objeto. Ejemplo: B x; A * ptr = & x; ptr-> f() // llama a la versión de clase A de f() y no a la versión de clase B de f() y eso es confuso. – TheChrisONeil

7

Prueba esto:

int main(){ 
    A *b = new B(); 
    b->f(); 
    return 0; 
} 

Creo que la respuesta será obvia una vez que vea el resultado ;-).

Sin ser virtual, el mecanismo de enlace tardío no se utilizará, por lo tanto, se utilizará la función definida para ese tipo de puntero, no la función de límite tardío que desea llamar. Esto lleva a toneladas de errores mal rastreables.

Por lo tanto, lo que está haciendo es crear una nueva función. Es puede ser lo que pretendía, pero alguien que lea su código después podría esperar que el código anterior funcione con enlace tardío. Muy confuso.

Una de las características que realmente quería ver es una advertencia en este caso, con una palabra clave "redefinir" para evitarlo, pero los sueños son sueños, y la realidad es la realidad -_-

2

El punto es que si, por ejemplo, tiene una lista de punteros a la clase base (List<A *> list) y luego llama al f(), no se llamará al método reimplementado en B.

Cuestiones relacionadas