Страница 1 из 1
Перегрузка виртуальных функций
Добавлено: 03 окт 2005, 16:42
michael
Имеем следующую программу:
test.cpp
Код: Выделить всё
class A
{
public:
virtual int U() {return 0;}
virtual int U(int n) {return n;}
};
class B: public A
{
public:
int U() {return U(1)+U(2);}
};
class C: public B
{
public:
int U(int n) {return n*n;}
};
int main(void)
{
C c;
return c.U();
}
Я думаю, что эта программа не содержит ошибок. Однако, компилятор (gcc 3.3.2) считает иначе
Код: Выделить всё
test.cpp: In member function `virtual int B::U()':
test.cpp:11: error: no matching function for call to `B::U(int)'
test.cpp:11: error: candidates are: virtual int B::U()
test.cpp:11: error: no matching function for call to `B::U(int)'
test.cpp:11: error: candidates are: virtual int B::U()
test.cpp: In function `int main()':
test.cpp:24: error: no matching function for call to `C::U()'
test.cpp:17: error: candidates are: virtual int C::U(int)
Если не делать перегрузки и функцию U(int n) всюду заменить на Un(int n), то компиляция проходит без ошибок и программа выдаёт ожидаемый результат. Может кто-нибудь прогнать этот пример на другой версии gcc или объяснить где ошибка?
Добавлено: 03 окт 2005, 17:17
slavaz
В классе B происходит переопределение метода U, причём переопределяются(скрываются) оба метода класса А. Чтобы юзать так, как ты хочешь, нужно написать:
Код: Выделить всё
class B: public A
{
public:
int U() {return A::U(1)+A::U(2);}
};
В классе C, соответственно, происходит переопределение метода U от классов A и B. Чтобы использовать метод(ы) родительского класса, нужно явно указать вызов метода родителя в потомке:
Код: Выделить всё
class C: public B
{
public:
int U() {return A::U();}
int U(int n) {return n*n;}
};
Добавлено: 04 окт 2005, 01:25
michael
Ты не понял задачу. Запишу класс A в другом виде
Код: Выделить всё
class A
{
public:
virtual int U()=0;
virtual int U(int n)=0;
};
Задача производных классов --- определить конкретные реализации функций. Это делается поэтапно. Сперва в классе B функция U() опрделяется через функции U(int n), затем в классе C определяется функция U(int n). Здесь, в целях простоты, приведена только часть реальной иерархии, которая выглядит примерно так:
Классы B и B' отличаются реализацией U() (в B' есть также и реализация U(int n) ). Классы C и C' отличаются реализацией U(int n). Цель: классы B', C и C' имеют единый интерфейс. А проблема в том, что вышеприведённый код не компилируется, если интерфейс задавать в виде пары перегруженных функций и прекрасно компилируется и работает, если функции имеют разные имена.
Добавлено: 04 окт 2005, 13:24
slavaz
А я тебе говорю, что если у родителя есть несколько методов с одинаковыми именами, но разными параметрами, то при появлении у потомка метода с таким же именем ВСЕ такие методы родителя скрываются и обращение к ним идёт ЯВНО, например РОДИТЕЛЬ::МЕТОД().
Причём не важно, виртуальные ли это функции родителя или нет (с виртуальными в любом случае нужно объявлять в потомке).
То есть:
Родитель:
int U(int)
int U(char)
int U()
Потомок:
int U(int)
Здесь баста, все методы U родитетеля скрылись и вызывать их в потомке теперь нужно явно. РОДИТЕЛЬ::U()
И в потомке перечислить
int U(char){ return РОДИТЕЛЬ::U(char);}
int U(){ return РОДИТЕЛЬ::U();}
...
Сделай по рекомендации - и код скомпилируется.
Добавлено: 04 окт 2005, 15:02
michael
Ага, понял. Значит, в классе B функция U(int) не видна, так как в нём определена собственная функция U(). Единственный выход, как я понимаю, не делать перегрузку. То, что предлагаешь ты, не годится, так как теряется смысл виртуальности. Программа компилируется, но выдаёт неверный результат. Если же функции класса A чисто виртуальные, то код вообще не компилируется.
Добавлено: 04 окт 2005, 15:40
slavaz
А так?
Код: Выделить всё
class A
{
public:
virtual int U() {return 0;}
virtual int U(int n) {return n;}
};
class B: public A
{
public:
virtual int U() {return U(1)+U(2);}
virtual int U(int n) {return A::U(n);}
};
class C: public B
{
public:
int U(int n) {return n*n;}
int U() {return B::U();}
};
int main(void)
{
C c;
return c.U();
}
результат - 5
Что и требовалось доказать
Добавлено: 04 окт 2005, 22:45
michael
Это действительно работает. Спасибо!