Несколько слов о перегрузке при наследовании
Напомню:
- Перегрузка - когда мы определяем метод с тем же именем, но другой сигнатурой (другие параметры)
- Переопределение - определяем метод с тем же именем и сигнатурой что и в родительском классе
Пример перегрузки:
class Parent
{
public:
void print() { std::cout << "Parent\n"; }
};
class Child : public Parent
{
public:
using Parent::print;
void print(int x) { std::cout << "Child\n"; }
};
Стоит обратить внимание, что для перегрузки обязательна строка using Parent::print;
, в ином случае дочерний метод просто перекроет родительский (Соответственно дочерний экземпляр будет способен вызвать только print(int x)
, а print()
- нет.
Попробуем по банальному переопределить метод (без использования виртуальных методов):
class Parent
{
public:
void print() { std::cout << "Parent\n"; }
};
class Child : public Parent
{
public:
void print() { std::cout << "Child\n"; }
};
Получается мы просто перекрыли дочерним методом родительский.
int main()
{
Parent * p = new Parent();
p->print(); // Parent
Child * c = new Child();
c->print(); // Child
Parent * pc = new Child();
pc->print(); // Parent
delete p;
delete c;
delete pc;
return 0;
}
Если в первых двух случаях все понятно: экземпляры вызывают соответствующие их классам методы, то с третим не все так очевидно. Мы создали указатель на родительский класс, однако он указывает на дочерний экземпляр (Если точнее, то этот указатель указывает только на Parent часть дочернего объекта и вызывать чисто дочерние методы через этот указатель не выйдет, только родительские). При этом вызываться будет все таки Parent::print()
.
Соответственно, решить эту проблему можно при помощи виртуальных методов - особый тип методов, который при вызове выполнит наиболее дочерний метод, который существует.
class Parent
{
public:
virtual void print() { std::cout << "Parent\n"; }
};
class Child : public Parent
{
public:
virtual void print() { std::cout << "Child\n"; }
};
int main()
{
Parent * p = new Parent();
p->print(); // Parent
Child * c = new Child();
c->print(); // Child
Parent * pc = new Child();
pc->print(); // Child
delete p;
delete c;
delete pc;
return 0;
}
Теперь все так же вызывается Parent::print()
, но так как он virtual, то компилятор понимает, что нужно проверить есть ли переопределения этого метода в дочернем классе, соответственно он найдёт Child::print()
.