2011-09-21 11 views
8

¿Alguien puede explicar el resultado del siguiente código?función virtual que es const en la clase base y no const en el derivado

#include <iostream> 
#include <string> 
class Animal 
{ 
public: 
    Animal(const std::string & name) : _name(name) { } 
    ~Animal() { } 
    virtual void printMessage() const 
    { 
     std::cout << "Hello, I'm " << _name << std::endl; 
    } 

private: 
    std::string _name; 
    // other operators and stuff 
}; 

class Cow : public Animal 
{ 
public: 
    Cow(const std::string & name) : Animal(name) { } 
    ~Cow() { } 
    virtual void printMessage() 
    { 
     Animal::printMessage(); 
     std::cout << "and moo " << std::endl; 
    } 
}; 

int main() { 
    Cow cow("bill"); 
    Animal * animal = &cow; 
    cow.printMessage(); 
    animal->printMessage(); 
} 

La salida es

Hola, soy Bill
y moo
Hola, soy Bill

no entiendo por qué. El puntero del animal apunta a un objeto de tipo Vaca. printMessage es una función virtual. ¿Por qué no es la implementación de la clase Cow la que se llama?

+0

+1 para un ejemplo de trabajo completo y mínimo – Flexo

+0

Ha intentado cambiar la función en la clase Cow para que sea 'virtual void printMessage() const'. –

Respuesta

12

Cow no está anulando la función virtual de Animal porque tiene una firma diferente. Lo que está sucediendo realmente es que Cow es que oculta la función en Animal.

El resultado de esto es que llamar printMessage en una Animal será sólo tiene que utilizar la versión en Animal, independientemente de la de Cow (no es reemplazar), pero llamando desde Cow utilizará el de Cow (porque oculta el de Animal).

Para solucionar este problema, quite el const en Animal, o agregar el const en Cow.

En C++ 2011, usted será capaz de utilizar la palabra clave override para evitar trampas sutiles como esto:

class Cow : public Animal 
{ 
public: 
    Cow(const std::string & name) : Animal(name) { } 
    ~Cow() { } 
    virtual void printMessage() override 
    { 
     Animal::printMessage(); 
     std::cout << "and moo " << std::endl; 
    } 
}; 

Aviso añadió el override después printMessage(). Esto causará que el compilador emita un error si printMessage no reemplaza una versión de clase base. En este caso, obtendrías el error.

+2

Si la función de clase base DEBE ser const y la derivada DEBE ser no const, puede utilizar un idioma de interfaz no virtual, en el que la función base no es virtual, pero llama a una virtual privada que no es const y por lo tanto puede ser anulado por la función no const en la clase derivada. – derpface

+0

gracias derpface, exactamente lo que necesito :) –

6

Tiene dos versiones diferentes de printMessage, una que es const y otra que no lo es. Los dos no están relacionados, a pesar de que tienen el mismo nombre. La nueva función en Cow oculta la de Animal, por lo que cuando la llama directamente, el compilador solo considera la versión Cow.

0

Acabo de probarlo. Agregue el const a la clase Cow y funcionará.

es decir, virtual void printMessage() const para ambas clases.

1

Me tomó un tiempo para entender de Peter Alexander ocultar respuesta, pero otra forma de entenderlo es como sigue:

decir que escrito mal el nombre del método en la clase de vaca, pero escrito correctamente en la clase de animales:

Animal::printMessage() 
Cow::mispelledPrintMessage() 

entonces cuando tiene una

Animal *animal; 

sólo se puede llamar

animal->printMessage(); 

pero no se puede llamar

animal->mispelledPrintMessage(); 

porque mispelledPrintMessage() no existe en la clase de animal. Es un nuevo método en la clase Cow, por lo que no se puede llamar polimórficamente a través de un puntero base.

Por lo que tener el método Animal tiene const en la firma, pero no en el método Cow es algo similar a un nombre de método ligeramente erróneo en la clase derivada.

PD: Otra cuarta solución (1 que hace ambos métodos const, 2 haciendo ambos métodos no const, o 3 usando la nueva palabra clave anulación 2011), es usar un molde, para forzar el puntero Animal en un puntero Cow:

((Cow*)animal)->printMessage(); 

Pero este es un HACK muy feo, y yo no lo recomendaría.

PS: Yo siempre trato de escribir mi toString() con métodos const en la firma en la clase base y todas las clases derivadas por esta misma razón. Además, tener const en la firma toString() te permite llamar a String() con un objeto const o no const. Si hubiera lugar dejado fuera del const, y trató de pasar a llamar a toString() con un objeto constante, el compilador GCC se quejaba con los descarta calificadores mensaje error:

const Bad' as `this' argument of `std::string Bad::toString()' discards qualifiers 

para este código:

#include <iostream> 
#include <string> 

class Bad 
{ 
public: 
     std::string toString() 
     { 
       return ""; 
     } 
}; 

int main() 
{ 
     const Bad  bad; 
     std::cout << bad.toString() << "\n"; 
     return 0; 
} 

Por lo tanto, como Cow no cambia ningún miembro de datos, la mejor solución es probablemente agregar const a printMessage() en la clase Cow derivada, de modo que las clases BASE Animal y DERIVED Cow tengan const en sus firmas .

-Dennis Bednar -ahd 310

1

corrección a cargo de Dennis.

Tenga en cuenta que, como solución, puede convertir al animal (que es un Animal *) en una Vaca *. Sin embargo, una vez que Cow * tu printMessage de la clase Animal no estará disponible. Por lo tanto ((Vaca *) animal) -> printMessage() debe ser ((Vaca *) animal) -> mispelledPrintMessage()

0

virtual en la subclase -> no es necesario. Simplemente agrega const a la función en la subclase para que sea la misma firma

Cuestiones relacionadas