Ho la mia risposta in forma di conversazione per essere una lettura migliore:
Perché abbiamo bisogno di funzioni virtuali?
A causa del polimorfismo.
Che cos'è il polimorfismo?
Il fatto che un puntatore di base può anche puntare a oggetti di tipo derivato.
In che modo questa definizione di polimorfismo porta alla necessità di funzioni virtuali?
Bene, attraverso l' associazione anticipata .
Che cos'è l'associazione precoce?
L'associazione anticipata (associazione in fase di compilazione) in C ++ significa che una chiamata di funzione viene fissata prima dell'esecuzione del programma.
Così...?
Pertanto, se si utilizza un tipo di base come parametro di una funzione, il compilatore riconoscerà solo l'interfaccia di base e se si chiama quella funzione con qualsiasi argomento proveniente da classi derivate, viene troncato, il che non è ciò che si desidera.
Se non è ciò che vogliamo che accada, perché è permesso?
Perché abbiamo bisogno del polimorfismo!
Qual è il vantaggio del polimorfismo allora?
È possibile utilizzare un puntatore del tipo di base come parametro di una singola funzione, quindi nel runtime del programma è possibile accedere a ciascuna delle interfacce del tipo derivato (ad esempio le relative funzioni membro) senza problemi, utilizzando la dereferenziazione di quella singola puntatore di base.
Ancora non so a cosa servano le funzioni virtuali ...! E questa è stata la mia prima domanda!
bene, questo è perché hai fatto la tua domanda troppo presto!
Perché abbiamo bisogno di funzioni virtuali?
Supponiamo di aver chiamato una funzione con un puntatore di base, che aveva l'indirizzo di un oggetto da una delle sue classi derivate. Come ne abbiamo parlato sopra, in fase di esecuzione, questo puntatore viene dereferenziato, finora tutto bene, tuttavia, ci aspettiamo che venga eseguito un metodo (== una funzione membro) "dalla nostra classe derivata"! Tuttavia, uno stesso metodo (uno che ha la stessa intestazione) è già definito nella classe base, quindi perché il tuo programma dovrebbe preoccuparsi di scegliere l'altro metodo? In altre parole, intendo, come puoi distinguere questo scenario da quello che prima vedevamo accadere normalmente?
La breve risposta è "una funzione membro virtuale nella base", e una risposta un po 'più lunga è che "a questo passaggio, se il programma vede una funzione virtuale nella classe base, sa (si rende conto) che stai cercando di usare polimorfismo "e così va per le classi derivate (usando v-table , una forma di associazione tardiva) per trovare un altro metodo con la stessa intestazione, ma con - inaspettatamente - un'implementazione diversa.
Perché un'implementazione diversa?
Testa di nocca! Vai a leggere un buon libro !
OK, aspetta aspetta aspetta, perché dovremmo preoccuparsi di usare i puntatori di base, quando lui / lei potrebbe semplicemente usare i puntatori di tipo derivato? Sii il giudice, ne vale la pena tutto questo mal di testa? Guarda questi due frammenti:
// 1:
Parent* p1 = &boy;
p1 -> task();
Parent* p2 = &girl;
p2 -> task();
// 2:
Boy* p1 = &boy;
p1 -> task();
Girl* p2 = &girl;
p2 -> task();
OK, anche se penso che 1 sia ancora meglio di 2 , potresti scrivere 1 in questo modo:
// 1:
Parent* p1 = &boy;
p1 -> task();
p1 = &girl;
p1 -> task();
e inoltre, dovresti essere consapevole che questo è ancora solo un uso forzato di tutte le cose che ti ho spiegato finora. Invece di questo, supponiamo ad esempio una situazione in cui avevi una funzione nel tuo programma che utilizzava rispettivamente i metodi di ciascuna delle classi derivate (getMonthBenefit ()):
double totalMonthBenefit = 0;
std::vector<CentralShop*> mainShop = { &shop1, &shop2, &shop3, &shop4, &shop5, &shop6};
for(CentralShop* x : mainShop){
totalMonthBenefit += x -> getMonthBenefit();
}
Ora prova a riscriverlo senza alcun mal di testa!
double totalMonthBenefit=0;
Shop1* branch1 = &shop1;
Shop2* branch2 = &shop2;
Shop3* branch3 = &shop3;
Shop4* branch4 = &shop4;
Shop5* branch5 = &shop5;
Shop6* branch6 = &shop6;
totalMonthBenefit += branch1 -> getMonthBenefit();
totalMonthBenefit += branch2 -> getMonthBenefit();
totalMonthBenefit += branch3 -> getMonthBenefit();
totalMonthBenefit += branch4 -> getMonthBenefit();
totalMonthBenefit += branch5 -> getMonthBenefit();
totalMonthBenefit += branch6 -> getMonthBenefit();
E in realtà, questo potrebbe essere ancora un esempio inventato!