// Example program
#include <iostream>
#include <string>
class A;
class B;
void h(A &a) { std::cout << "a"; }
void h(B &b) { std::cout << "b"; }
class A {
public:
virtual void call() { h(*this); }
};
class B: public A {
public:
void call() override { h(*this); }
};
int main()
{
B b = B{};
A &test = b;
h(test); // prints a
test.call(); // prints b
}
Я немного возился с кодом и заметил, что *this
, казалось, всегда знал, какой это подкласс, тогда как обычно мы «теряем информацию», когда инициализируем подкласс как объект суперкласса.
Что именно происходит за кулисами, чтобы заставить h(*this)
звонить h(B &b)
вместо h(A &a)
? Что два вызова в main
делают по-разному, так что один печатает a, а другой печатает b?
Да, это видно из поведения программы, но меня больше смущало, почему это так.
Определите, какая функция вызывается в test.call();
, A::call
или B::call
, и вы получите ответ.
Метод virtual
использует динамическую диспетчеризацию. B отменяет call
.
h(test); // prints a
Поскольку тип test
— A &
, вызывается именно эта перегрузка. Здесь больше нечего сказать. Конец. Книги по этому вопросу закрыты, и здесь это задача компилятора. Компилятор определяет, какую перегрузку вызывать, основываясь на том, что такое test
.
test.call();
Это вызывает виртуальный метод test
. Поскольку test
на самом деле является B
, это вызывает переопределенный виртуальный метод B
. Так работают виртуальные методы в C++.
Угадайте, что происходит в B
?
void call() override { h(*this); }
Поскольку тип *this
— B &
, вызывается именно эта перегрузка. Здесь больше нечего сказать. Конец. Книги по этому вопросу закрыты, и это задача компилятора. Компилятор определяет, какую перегрузку вызывать, основываясь на том, что такое *this
.
В методе
call
метод знает, что тип*this
в контексте.