Tengo mi respuesta en forma de conversación para que se lea mejor:
¿Por qué necesitamos funciones virtuales?
Por el polimorfismo.
¿Qué es el polimorfismo?
El hecho de que un puntero base también puede apuntar a objetos de tipo derivados.
¿Cómo conduce esta definición de polimorfismo a la necesidad de funciones virtuales?
Bueno, a través de la unión temprana .
¿Qué es la unión temprana?
El enlace temprano (enlace en tiempo de compilación) en C ++ significa que una llamada de función se repara antes de que se ejecute el programa.
Entonces...?
Entonces, si usa un tipo base como parámetro de una función, el compilador solo reconocerá la interfaz base, y si llama a esa función con cualquier argumento de las clases derivadas, se corta, que no es lo que desea que suceda.
Si no es lo que queremos que suceda, ¿por qué está permitido?
¡Porque necesitamos el polimorfismo!
¿Cuál es el beneficio del polimorfismo entonces?
Puede usar un puntero de tipo base como parámetro de una sola función, y luego, en el tiempo de ejecución de su programa, puede acceder a cada una de las interfaces de tipo derivadas (por ejemplo, sus funciones miembro) sin ningún problema, utilizando la desreferenciación de ese único puntero base.
¡Todavía no sé para qué funciones virtuales son buenas ...! ¡Y esta fue mi primera pregunta!
bueno, ¡esto es porque hiciste tu pregunta demasiado pronto!
¿Por qué necesitamos funciones virtuales?
Suponga que llamó a una función con un puntero base, que tenía la dirección de un objeto de una de sus clases derivadas. Como hemos mencionado anteriormente, en el tiempo de ejecución, este puntero se desreferencia, hasta ahora muy bien, sin embargo, ¡esperamos que se ejecute un método (== una función miembro) "de nuestra clase derivada"! Sin embargo, un mismo método (uno que tiene el mismo encabezado) ya está definido en la clase base, entonces ¿por qué su programa debería molestarse en elegir el otro método? En otras palabras, quiero decir, ¿cómo puedes distinguir este escenario de lo que solíamos ver que normalmente sucede antes?
La respuesta breve es "una función miembro virtual en base", y una respuesta un poco más larga es que, "en este paso, si el programa ve una función virtual en la clase base, sabe (se da cuenta) que está tratando de usar polimorfismo "y así va a clases derivadas (usando v-table , una forma de enlace tardío) para encontrar ese otro método con el mismo encabezado, pero con -esperablemente- una implementación diferente.
¿Por qué una implementación diferente?
¡Cabeza de nudillo! ¡Ve a leer un buen libro !
OK, espera, espera, ¿por qué uno se molestaría en usar punteros de base, cuando él / ella podría simplemente usar punteros de tipo derivado? Usted sea el juez, ¿vale la pena todo este dolor de cabeza? Mire estos dos fragmentos:
// 1:
Parent* p1 = &boy;
p1 -> task();
Parent* p2 = &girl;
p2 -> task();
// 2:
Boy* p1 = &boy;
p1 -> task();
Girl* p2 = &girl;
p2 -> task();
OK, aunque creo que 1 es aún mejor que 2 , podrías escribir 1 como este:
// 1:
Parent* p1 = &boy;
p1 -> task();
p1 = &girl;
p1 -> task();
y, además, debes tener en cuenta que esto es solo un uso artificial de todas las cosas que te he explicado hasta ahora. En lugar de esto, suponga, por ejemplo, una situación en la que tuvo una función en su programa que utilizó los métodos de cada una de las clases derivadas respectivamente (getMonthBenefit ()):
double totalMonthBenefit = 0;
std::vector<CentralShop*> mainShop = { &shop1, &shop2, &shop3, &shop4, &shop5, &shop6};
for(CentralShop* x : mainShop){
totalMonthBenefit += x -> getMonthBenefit();
}
¡Ahora, intenta reescribir esto, sin ningún dolor de cabeza!
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();
Y, de hecho, ¡este podría ser un ejemplo artificial!