У меня есть мой ответ в форме беседы, чтобы быть лучше прочитанным:
Зачем нам нужны виртуальные функции?
Из-за полиморфизма.
Что такое полиморфизм?
Тот факт, что базовый указатель также может указывать на объекты производного типа.
Как это определение полиморфизма приводит к необходимости виртуальных функций?
Ну, через раннее связывание .
Что такое раннее связывание?
Раннее связывание (связывание во время компиляции) в C ++ означает, что вызов функции фиксируется перед выполнением программы.
Так...?
Таким образом, если вы используете базовый тип в качестве параметра функции, компилятор распознает только базовый интерфейс, и если вы вызываете эту функцию с любыми аргументами из производных классов, она отсекается, что не должно происходить.
Если это не то, что мы хотим, почему это разрешено?
Потому что нам нужен полиморфизм!
Какая польза от полиморфизма?
Вы можете использовать указатель базового типа в качестве параметра отдельной функции, а затем во время выполнения вашей программы вы можете получить доступ к каждому из интерфейсов производного типа (например, их функции-члены) без каких-либо проблем, используя разыменование этого единственного объекта. базовый указатель.
Я до сих пор не знаю, для чего нужны виртуальные функции ...! И это был мой первый вопрос!
ну, это потому что ты задал свой вопрос слишком рано!
Зачем нам нужны виртуальные функции?
Предположим, что вы вызвали функцию с базовым указателем, у которого был адрес объекта из одного из его производных классов. Как мы говорили об этом выше, во время выполнения этот указатель разыменовывается, но пока все хорошо, однако мы ожидаем, что метод (== функция-член) «из нашего производного класса» будет выполнен! Однако в базовом классе уже определен тот же метод (с тем же заголовком), так почему ваша программа должна выбрать другой метод? Другими словами, я имею в виду, как вы можете отличить этот сценарий от того, что мы обычно видели раньше?
Краткий ответ - «Виртуальная функция-член в базе», и немного более длинный ответ - «на этом этапе, если программа видит виртуальную функцию в базовом классе, она знает (понимает), что вы пытаетесь использовать «полиморфизм» и т. д. переходит к производным классам (используя v-таблицу , форму позднего связывания), чтобы найти этот другой метод с тем же заголовком, но, как ожидается, с другой реализацией.
Почему другая реализация?
Ты с головой! Иди почитай хорошую книгу !
Хорошо, подожди, подожди, подожди, зачем использовать базовые указатели, если он / она может просто использовать указатели производного типа? Будь судьей, стоит ли вся эта головная боль? Посмотрите на эти два фрагмента:
// 1:
Parent* p1 = &boy;
p1 -> task();
Parent* p2 = &girl;
p2 -> task();
// 2:
Boy* p1 = &boy;
p1 -> task();
Girl* p2 = &girl;
p2 -> task();
Хорошо, хотя я думаю, что 1 все еще лучше, чем 2 , вы могли бы написать 1 так же:
// 1:
Parent* p1 = &boy;
p1 -> task();
p1 = &girl;
p1 -> task();
и, кроме того, вы должны знать, что это все еще надуманное использование всего того, что я вам объяснил до сих пор. Вместо этого, предположим, например, ситуацию, в которой у вас была функция в вашей программе, которая использовала методы из каждого из производных классов соответственно (getMonthBenefit ()):
double totalMonthBenefit = 0;
std::vector<CentralShop*> mainShop = { &shop1, &shop2, &shop3, &shop4, &shop5, &shop6};
for(CentralShop* x : mainShop){
totalMonthBenefit += x -> getMonthBenefit();
}
Теперь попробуйте переписать это без головной боли!
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();
И на самом деле, это может быть еще и надуманным примером!