2011-09-21 18 views
8

Qualcuno può spiegare l'output del seguente codice?funzione virtuale che è const nella classe base e non const nel derivato

#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(); 
} 

L'uscita è

Ciao, sono fattura
e moo
Ciao, sono fattura

non capisco perché. L'animale puntatore indica un oggetto di tipo Mucca. printMessage è una funzione virtuale. Perché l'implementazione della classe Cow non è quella che viene chiamata?

+0

+1 per un esempio di lavoro completo e minimo – Flexo

+0

Si è tentato di modificare la funzione nella classe Cow come 'virtual void printMessage() const'. –

risposta

12

Cow non sovrascrive la funzione virtuale da Animal perché ha una firma diversa. Quello che accade in realtà è che Cow è che nasconde la funzione in Animal.

Il risultato di questo è che chiamando printMessage su un Animal sarà solo utilizzare la versione in Animal, a prescindere di quello di Cow (non è prevalente), ma chiamandolo da Cow utilizzerà quello in Cow (perché nasconde quello da Animal).

Per risolvere questo problema, rimuovere il const in Animal, o aggiungere il const in Cow.

In C++ 2011, si sarà in grado di utilizzare la parola chiave override per evitare trappole sottili come questo:

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; 
    } 
}; 

Avviso l'aggiunta override dopo printMessage(). Ciò farà sì che il compilatore emetta un errore se lo printMessage non sovrascrive una versione della classe base. In questo caso, si otterrebbe l'errore.

+2

Se la funzione della classe base DEVE essere const e quella derivata DEVE essere non-const, è possibile utilizzare un idioma di interfaccia non virtuale, in cui la funzione di base non è virtuale, ma chiama un privato virtuale che non è const e quindi può essere sovrascritto dalla funzione non-const nella classe derivata. – derpface

+0

grazie derpface, esattamente quello che mi serve :) –

6

Si dispone di due versioni diverse di printMessage, una che è const e una che non lo è. I due non sono correlati, anche se hanno lo stesso nome. La nuova funzione in Cow nasconde quella in Animal, quindi quando la si chiama direttamente il compilatore considera solo la versione Cow.

0

Appena provato. Aggiungi il const alla classe Cow e funzionerà.

, ad esempio virtual void printMessage() const per entrambe le classi.

1

Mi c'è voluto un po 'per capire di Peter Alexander nasconde risposta, ma un altro modo per capire è come segue:

dire che si mispelled il nome del metodo nella classe mucca, ma digitato correttamente in classe degli animali:

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

poi quando si dispone di un

Animal *animal; 
.210

è possibile chiamare solo

animal->printMessage(); 

ma non si può chiamare

animal->mispelledPrintMessage(); 

perché mispelledPrintMessage() non esiste nella classe degli animali. Il suo un nuovo metodo nella classe mucca, quindi non possono essere polimorfico chiamati attraverso un puntatore base.

Quindi avere il metodo Animal ha const nella firma, ma non nel metodo Cow è un po 'analogo a un nome di metodo leggermente mispelled nella classe derivata.

PS: Un altro quarto soluzione (1 rendendo entrambi i metodi const, 2 rendendo entrambi i metodi non-const, o 3 usando nuova parola chiave 2011 override), è quello di utilizzare un cast, per forzare il puntatore animali in un puntatore Cow:

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

Ma questo è un HACK molto brutto, e non lo consiglierei.

PS: cerco sempre di scrivere il mio toString() metodi con const nella firma nella classe base e tutte le classi derivate proprio per questo motivo. Inoltre, avere const nella firma toString() consente di chiamare toString() con un oggetto const-o non-const. Se aveste invece lasciato fuori il const, e ha cercato di passare chiamare toString() con un oggetto const, compilatore GCC si lamentava con i scarta qualificazioni messaggio di errore:

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

per questo codice:

Quindi, in conclusione, poiché Cow non modifica alcun membro dati, la soluzione migliore probabilmente è aggiungere const a printMessage() nella classe Cow derivata, in modo che sia le classi BASE Animal che DERIVED Cow abbiano const nelle loro firme .

-Dennis Bednar -ahd 310

1

correzione al post di Dennis.

Nota si affermano come una soluzione si può lanciare l'animale (che è un * Animale) ad una mucca *. Tuttavia, una volta che Cow * il tuo printMessage dalla classe Animal non sarà disponibile. Quindi ((Vacca *) animale) -> printMessage() deve essere ((Cow *) animale) -> mispelledPrintMessage()

0

virtuale in sottoclasse -> nessuna necessità. È sufficiente aggiungere const per funzionare in sottoclasse per rendere la stessa firma

Problemi correlati