Понимание (просто?) Наследование С++
Я немного пытаюсь понять, почему этот фрагмент кода не компилируется.
#include <cstdio>
class A {
public:
virtual int potential()=0;
virtual int potential(int arg, int arg2)=0;
};
class B : public A {
public:
int potential() { return 1; }
virtual int potential(int arg, int arg2) { return 2; }
};
class C : public B {
public:
int potential(int arg, int arg2) { return 3; }
};
int main(int argc, char** argv) {
C c;
int value = c.potential();
printf("Got %i\n", value);
return 0;
}
У меня есть два чистых виртуальных метода, названных potential
в абстрактном суперклассе A
. Подкласс B
затем определяет оба, но дополнительный подкласс C
должен только переопределить один из методов.
Однако при компиляции распознается только метод, определенный в C
, и potential()
не отображается (это должно быть унаследовано от B
):
In function 'int main(int, char**)':
Line 23: error: no matching function for call to 'C::potential()'
compilation terminated due to -Wfatal-errors.
Если я переименую A::potential(int, int)
на что-то еще полностью вниз по дереву наследования, например A::somethingElse(int, int)
, тогда код компилируется отлично, а результат Got 1
, как и ожидалось.
Это подтверждено с помощью clang, g++ и MSVC cl.
Любые идеи о том, что происходит?
Ответы
Ответ 1
Однако при компиляции распознается только метод, определенный в C, а потенциал() не отображается (это должно быть унаследовано от B).
С++ не работает следующим образом: поскольку в C
вы реализовали другой метод potential
(метод с тем же именем, но с разными параметрами), другой метод скрыт до C
.
Скрытие происходит из-за того, как С++ разрешает (перегружает) имена методов: когда вы вызываете метод potential
в экземпляре класса (здесь C
), С++ ищет в классе, является ли метод этого имени существует. Если это не так, он продолжает поиск в базовых классах. Он продвигается в иерархии до тех пор, пока не будет найден хотя бы один метод этого имени.
Но в вашем случае С++ не нужно искать далеко: метод уже существует в C
, поэтому он останавливает поиск. Теперь С++ пытается сопоставить подпись метода. К сожалению, подпись метода не соответствует, но в это время ее слишком поздно: разрешение перегрузки выходит из строя; С++ не ищет другие методы, которые могут совпадать.
Существует три решения:
-
Импортируйте его с помощью using
в C:
class C : public B {
public:
using B::potential;
int potential(int arg, int arg2) { return 3; }
};
-
Вызвать метод из экземпляра базового класса в main
:
C c;
B& b = c;
int value = b.potential();
-
Определите имя явно в main
:
C c;
int value = c.B::potential();
Ответ 2
Проблема заключается в скрытии имени.
Перегрузки функций и наследование функций не являются лучшими друзьями. Обычно вы или [хмм, что "либо" для трех?]:
- Перегрузка функции в пределах одного класса. Все работает нормально.
- Наследовать неперегруженную функцию из базового класса. Все работает нормально.
- Восстановить неперегруженную функцию из базового класса
B
в производном классе C
. C::func
скрывает B::func
, потому что имеет одно и то же имя.
Вы используете наследование и перегрузку, а ваш C::func
скрывает B::func
и каждую перегрузку, даже те, которые не были повторно реализованы в C
.
Это немного странная путаница С++, но она легко разрешается.
Вкратце, решение состоит в том, чтобы принести B::potential()
в область видимости для C
с помощью оператора using
:
class C : public B {
public:
using B::potential; // brings overloads from B into scope
int potential(int arg, int arg2) { return 3; }
};
Я написал статью здесь, которая демонстрирует проблему в глубину.