В ответе выводит 3, потому что функция f - это виртуальная функция, переопределенная в наследуемом классе. При этом переменная p - это указатель на экземпляр класса B, который инициализируется экземпляром класса C. Класс C наследуется от B, поэтому в таблице виртуальных функций f указывает на функцию именно класса C, а не B. В итоге через указатель p вызывается функция класса C. В промышленном программировании применений много. Например, когда требуется изменять поведение во время выполнения. Создается базовый класс, который реализует некий интерфейс (функция f). Создаются классы наследники, каждый из которых реализует интерфейсную функцию по своему. Переменная - указатель на базовый класс хранит экземпляр с требуемым поведением и может быть изменена во время выполнения для изменения поведения. Вот пример:
#include <iostream>
using namespace std;
class Animal{
public:
virtual void say() = 0;
};
class Cat : public Animal{
public:
void say(){
cout << "Мяу" << endl;
}
};
class Dog : public Animal{
public:
void say(){
cout << "Гав" << endl;
}
};
int main() {
Cat cat;
Dog dog;
Animal *a;
a = &cat;
a->say();
a = &dog;
a->say();
return 0;
}
в результате животное сначала скажет Мяу, а потом Гав