0

Уважаемые коллеги! В одном соревновании столкнулся с таким кодом:

class A {
public:
virtual void f(){cout<<1;}
};

class B: public A {
public:
void f(){cout<<2;}
};

class C: public B {
public:
void f(){cout<<3;}
}; 

int main() {
B *p = new C;
p->f();
}

OUTPUT: 3

Вопрос: Как вообще работает это? Почему выводит тройку, а не что-то ещё? И как называется (зачем используется в промышленном программировании) такая конструкция как:

 B *p = new C;
  p->f();
  • а почему нет?) функция то виртуальная. – pavel Oct 02 '17 at 08:04
  • и что это означает? Вы понимаете - так дайте развёрнутый ответ. – Andrew Kachalin Oct 02 '17 at 08:05
  • 1
    как это работает - это написано в любом более-менее внятной книге по с++. Зачем? это тоже там же будет написано. Пробовали читать? – KoVadim Oct 02 '17 at 08:09
  • @KoVadim дубль https://ru.stackoverflow.com/questions/454263/%d0%9a%d0%bd%d0%b8%d0%b3%d0%b8-%d0%b8-%d1%83%d1%87%d0%b5%d0%b1%d0%bd%d1%8b%d0%b5-%d1%80%d0%b5%d1%81%d1%83%d1%80%d1%81%d1%8b-%d0%bf%d0%be-%d0%a1? – pavel Oct 02 '17 at 08:10
  • формально - дубль. Но очень тонкий – KoVadim Oct 02 '17 at 08:11

1 Answers1

3

В ответе выводит 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;
}

в результате животное сначала скажет Мяу, а потом Гав

tilin
  • 2,840
  • "в таблице виртуальных функций f указывает на функцию именно класса C " - а я считаю что B. Только компилятор почему-то согласен с вами и вот это-то мне и непонятно. Если можете, поясните подробнее. – Andrew Kachalin Oct 02 '17 at 08:17
  • @AndrewKachalin на С. таблица делается по реальному типу объекта (по new) а не по переменной где он хранится – pavel Oct 02 '17 at 08:18
  • @tilin Ну ладно. Если не будет других, более развернутых ответов, значит можно принять ваш. – Andrew Kachalin Oct 02 '17 at 08:19
  • 2
    Да куда уж развернутее. – ffk Oct 02 '17 at 08:25
  • 1
    @AndrewKachalin, "а я считаю что B". Вы выполнили раннее связывание. То есть выбрали реализацию в соответствии с типом указателя. Будь функция невиртуальной, компилятор повел бы себя точно так же. Для виртуальных функций, компилятор выполняет позднее связывание. То есть ищет нужную реализацию на этапе выполнения в таблице виртуальных функций – yrHeTateJlb Oct 02 '17 at 11:38
  • @AndrewKachalin, короче, чтобы разобраться откуда ноги растут, гуглите "позднее связывание" и "динамический полиморфизм" – yrHeTateJlb Oct 02 '17 at 11:40