Этот сайт использует cookies. Продолжение работы с сайтом означает, что Вы согласны!
Виртуальные методы
Если имя метода в производном классе совпадает с именем метода из базового класса, то будет использоваться метод из производного класса. В этом случае тип и количество параметров у методов должны совпадать, в противном случае производится перегрузка метода, а не переопределение. Чтобы вызвать метод базового класса из метода производного класса, следует указать перед методом название базового класса и оператор ::
. Рассмотрим переопределение методов на примере (листинг 13.29).
Листинг 13.29. Переопределение методов
#include <iostream>
class A {
public:
void func();
};
class B : public A {
public:
void func();
};
void A::func() { std::cout << "A::func()" << std::endl; }
void B::func() {
std::cout << "B::func()" << std::endl;
// A::func(); // Вызов метода из базового класса
}
int main() {
B obj;
obj.func(); // B::func()
return 0;
}
В этом примере в классах A
и B
объявлен метод func()
. Класс B
наследует класс A
. Следовательно, метод из класса B
переопределяет одноименный метод из класса A
. Поэтому при создании объекта класса B
будет вызван метод именно из производного класса. Таким образом достигается статический полиморфизм.
Язык C++ поддерживает также динамический полиморфизм, при котором выбор вызываемого метода зависит от объекта, на который ссылается указатель, имеющий тип базового класса. В этом случае используется позднее связывание, которое означает, что выбор метода осуществляется в процессе выполнения программы, а не на этапе компиляции. Чтобы при использовании указателя (имеющего тип базового класса) ссылающегося на объект производного класса был вызван одноименный метод из производного класса, необходимо объявить метод в базовом классе виртуальным. Для этого перед объявлением метода следует добавить ключевое слово virtual
.
При наследовании дублировать ключевое слово virtual
в производных классах не нужно, так как виртуальные свойства метода автоматически передаются всем производным классам. Виртуальный метод не обязательно замещать в производном классе. Если виртуальный метод не замещен, то используется метод базового класса. Нельзя объявлять виртуальными статические методы, дружественные функции, а также конструкторы. Деструктор класса нужно обязательно сделать виртуальным, если существует хотя бы один виртуальный метод. Таким образом, любой метод, который может быть замещен в производном классе, следует объявить виртуальным, хотя увлекаться этим не стоит, так как при использовании виртуальных методов уменьшается эффективность работы программы. Переделаем код из листинга 13.29 и сделаем метод func()
в базовом классе виртуальным (листинг 13.30).
Листинг 13.30. Виртуальные методы
#include <iostream>
class A {
public:
virtual void func(); // Виртуальный метод
virtual ~A() {} // Деструктор также должен быть виртуальным
};
class B : public A {
public:
void func() override;
};
void A::func() { std::cout << "A::func()" << std::endl; }
void B::func() { std::cout << "B::func()" << std::endl; }
void test(A &obj) { obj.func(); }
int main() {
A *pA, a;
B b;
pA = &a; // Адрес базового класса
pA->func(); // A::func()
pA = &b; // Адрес производного класса
pA->func(); // B::func()
test(a); // A::func()
test(b); // B::func()
return 0;
}
В этом примере дополнительно определена функция test()
, которая в качестве параметра принимает ссылку на объект базового класса. Передать в функцию можно объект базового класса, а также объект любого производного класса. Выбор вызываемого внутри функции метода зависит от типа переданного объекта.
В процессе переопределения метода мы можем что-нибудь напутать и создать одноименный метод, но с другой сигнатурой. В результате мы получим перегрузку метода, а не переопределение. Компилятор ведь не знает, что мы хотим сделать, а перегрузка — это допустимая операция, и никаких ошибок здесь нет. Чтобы сообщить компилятору о переопределении, после списка параметров следует указать спецификатор override
. Увидев спецификатор override
, компилятор проверит наличие метода с указанной сигнатурой в базовых классах. Если такого метода нет, то выведет сообщение об ошибке. В результате мы застрахуем себя от ошибочных действий. Пример указания спецификатора:
void func() override;
override
не является зарезервированным ключевым словом.Реквизиты
ЮMoney (Yandex-деньги): 410011140483022
ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов