Перегрузка метода множественного наследования C++

#include <iostream>

using namespace std;

class Base1 {
public:
    virtual void f(int n) = 0;
};

class Base2 {
public:
    virtual void f(char *s) = 0;
};

class Derive1 : public Base1, public Base2 {
public:
    void f(int n) { cout << "d1 fn" << endl; }
    void f(char *s) { cout << "d1 fs" << endl; }
};

class Derive2 : public Derive1 {
public:
    void f(int n) { cout << "d2 fn" << endl; }
    void f(char *s) { cout << "d2 fs" << endl; }
};

int main() {
    Derive1 *d1 = new Derive2();
    int n = 0;
    char *s = "";
    d1->f(n);
    d1->f(s);
    return 0;
}

Приведенный выше код работает, как и ожидалось, но если я закомментирую один из методов Derive1, я получу ошибку преобразования; если я закомментирую оба метода Derive1, я получу ошибку неоднозначности методов.

Что меня смутило, так это то, почему Derive1 должен определять эти два метода, почему определение их только в Derive2 не работает. Мне нужна помощь, чтобы понять это.

Некоторые уточнения:

  1. Предположим, я никогда не хочу создавать экземпляры Derive1. Итак, совершенно нормально, если Derive1 является абстрактным классом.

  2. «Все чистые виртуальные функции должны иметь определение в производном классе». Это неверно, если я не хочу создавать экземпляры этого производного класса.

  3. Если я изменю f в Base1 на f1, а f в Base2 на f2 (просто изменю имена), то Derive1 не нужно определять ни один из них, достаточно определить f1 и f2 в Derive2.

Итак, с поддержкой перегрузки методов, как мне показалось, в приведенном выше коде я объявил функцию с именем вроде f_int в Base1; в Base2 я объявил функцию с именем вроде f_str. Именно так компилятор реализует перегрузку методов, верно? Но похоже, что это не так.


person user3511089    schedule 22.05.2015    source источник


Ответы (3)


Derive1 является производным классом от Base1 и Base2. Производные классы должны реализовывать все чистые виртуальные функции своих базовых классов.

Derive1 не помогает то, что Derive2 реализует эти функции, потому что можно создать экземпляр Derive1, а для этого он должен реализовать все унаследованные чисто виртуальные методы.

Например, если бы вы не реализовали функции в Derive1, какого поведения вы ожидали бы от этого?

Derive1* d1 = new Derive1;
int n = 0;
char *s = "";
d1->f(n);
d1->f(s);

Derive1 не реализовал их, а d1 не является экземпляром Derive2, так что же он должен делать?

person Cory Kramer    schedule 22.05.2015
comment
Производные классы должны реализовывать все чистые виртуальные функции. - person Halil Kaskavalci; 22.05.2015
comment
Спасибо, я добавил это уточнение в свой пост. - person Cory Kramer; 22.05.2015
comment
Конкретные классы должны реализовывать все чисто виртуальные методы из своих базовых классов. Промежуточный абстрактный класс не нужен. Хотя в данном случае это не проблема. - person Mark B; 22.05.2015
comment
Предположим, я никогда не хочу создавать какие-либо экземпляры Derive1, мне все еще нужно определить все чистые виртуальные функции, которые унаследовал Derive1? - person user3511089; 23.05.2015

Если вы объявите чистую виртуальную функцию в классе, она превратится в абстрактный класс, и вы не сможете создать ее экземпляр. И все чистые виртуальные функции должны иметь определение в производном классе. Оба ваших базовых класса имеют чисто виртуальные функции, поэтому ваш производный1 должен определять чисто виртуальные функции базовых классов.

person Steephen    schedule 22.05.2015

Начнем с удаления обоих в Derived1:

Когда вы вызываете d1->f, вы не создали ничего конкретного для этого класса, поэтому он обращается к его базовым классам. Каждый базовый класс будет рассматриваться как отдельный набор кандидатов. Итак, сначала он проверяет base1 и видит f, затем проверяет base2 и видит еще один f. Компилятор не может выбрать, какой из них вызывать, поэтому вызов неоднозначен. Вы можете using родительские функции преобразовать в Derived1, если не хотите их повторно реализовывать, или дать компилятору подсказку о том, какую базу использовать.

Если вы закомментируете только одну из функций в Derived1, та, которую вы оставите, скроет обе родительские версии, не позволяя выбрать одну из них. На этом этапе вы можете вызвать тот, который вы определили, и не можете вызвать другой, если только вы снова не сообщите компилятору, из какого конкретного базового класса вы хотите его вызвать.

person Mark B    schedule 22.05.2015
comment
С поддержкой перегрузки методов, думаю, при проверке Base1 компилятор должен увидеть что-то вроде f_int; при проверке Base2 компилятор должен увидеть что-то вроде f_str. Итак, эти две f-функции — это как бы две разные функции, тогда почему одна скрывает другую? - person user3511089; 23.05.2015