In C ++, una funzione è automaticamente virtuale se sostituisce una funzione virtuale?
-
05-07-2019 - |
Domanda
Mi aspetto che se foo
viene dichiarato nella classe D
, ma non contrassegnato come virtuale, il seguente codice chiamerebbe l'implementazione di foo
in D
(indipendentemente dal tipo dinamico di d
).
D& d = ...;
d.foo();
Tuttavia, nel seguente programma, non è così. Qualcuno può spiegare questo? Un metodo è automaticamente virtuale se sostituisce una funzione virtuale?
#include <iostream>
using namespace std;
class C {
public:
virtual void foo() { cout << "C" << endl; }
};
class D : public C {
public:
void foo() { cout << "D" << endl; }
};
class E : public D {
public:
void foo() { cout << "E" << endl; }
};
int main(int argc, char **argv)
{
E& e = *new E;
D& d = *static_cast<D*>(&e);
d.foo();
return 0;
}
L'output del programma sopra è:
E
Soluzione
Lo standard 10.3.2 (class.virtual) dice:
Se una funzione membro virtuale vf viene dichiarata in una classe Base e in una classe Derivata, derivata direttamente o indirettamente da Base, viene dichiarata una funzione membro vf con lo stesso nome e lo stesso elenco di parametri di Base :: vf, quindi Derivata :: vf è anche virtuale (indipendentemente dal fatto che sia così dichiarato) e ignora *
[Nota a piè di pagina: una funzione con lo stesso nome ma un elenco di parametri diverso (clausola sopra) come funzione virtuale non è necessariamente virtuale e non sostituisce. L'uso dello specificatore virtuale nella dichiarazione di una funzione prioritaria è legale ma ridondante (ha una semantica vuota). Il controllo dell'accesso (clausola class.access) non è considerato nel determinare l'override. --- end foonote]
Altri suggerimenti
La risposta rapida potrebbe essere no, ma la risposta corretta è sì
Il C ++ non sa come nascondere le funzioni, quindi sovrascrivendo la funzione virtuale senza segni di parole chiave virtuali che funzionano anche in modo virtuale.
Non stai creando alcuna copia dell'oggetto di e e lo stai mettendo in d. Quindi d.foo () segue il normale comportamento polimorfico e chiama il metodo di classe derivato. Un metodo dichiarato virtuale nella classe base diventa automaticamente virtuale anche nella classe derivata.
L'output (" E ") si comporta esattamente come ci si aspetterebbe che si comporti.
Il motivo: Il tipo dinamico (ovvero runtime) di quel riferimento è E. Stai facendo un upcast statico su D, ma questo non cambia il tipo reale dell'oggetto ovviamente.
È proprio questa l'idea alla base dei metodi virtuali e degli invii dinamici: vedi il comportamento del tipo che stavi istanziando, che è E, in questo caso.