Skip to the content.

Несколько слов о перегрузке при наследовании

Напомню:

Пример перегрузки:

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().