Перегрузка виртуальных функций

Все о программировании под *nix
michael
Неотъемлемая часть форума
Сообщения: 434
Зарегистрирован: 12 апр 2004, 11:00
Откуда: г. Владивосток
Контактная информация:

Перегрузка виртуальных функций

Сообщение 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 или объяснить где ошибка?

slavaz
Фанатеющий
Сообщения: 155
Зарегистрирован: 20 июл 2005, 15:22
Откуда: Brest
Контактная информация:

Сообщение 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;}
};
just for fun

michael
Неотъемлемая часть форума
Сообщения: 434
Зарегистрирован: 12 апр 2004, 11:00
Откуда: г. Владивосток
Контактная информация:

Сообщение michael »

Ты не понял задачу. Запишу класс A в другом виде

Код: Выделить всё

class A
{
 public:
 virtual int U()=0;
 virtual int U(int n)=0;
};
Задача производных классов --- определить конкретные реализации функций. Это делается поэтапно. Сперва в классе B функция U() опрделяется через функции U(int n), затем в классе C определяется функция U(int n). Здесь, в целях простоты, приведена только часть реальной иерархии, которая выглядит примерно так:

Код: Выделить всё

    A
   / \
  B  B'
 / \
C  C'
Классы B и B' отличаются реализацией U() (в B' есть также и реализация U(int n) ). Классы C и C' отличаются реализацией U(int n). Цель: классы B', C и C' имеют единый интерфейс. А проблема в том, что вышеприведённый код не компилируется, если интерфейс задавать в виде пары перегруженных функций и прекрасно компилируется и работает, если функции имеют разные имена.

slavaz
Фанатеющий
Сообщения: 155
Зарегистрирован: 20 июл 2005, 15:22
Откуда: Brest
Контактная информация:

Сообщение slavaz »

А я тебе говорю, что если у родителя есть несколько методов с одинаковыми именами, но разными параметрами, то при появлении у потомка метода с таким же именем ВСЕ такие методы родителя скрываются и обращение к ним идёт ЯВНО, например РОДИТЕЛЬ::МЕТОД().
Причём не важно, виртуальные ли это функции родителя или нет (с виртуальными в любом случае нужно объявлять в потомке).

То есть:
Родитель:
int U(int)
int U(char)
int U()

Потомок:
int U(int)

Здесь баста, все методы U родитетеля скрылись и вызывать их в потомке теперь нужно явно. РОДИТЕЛЬ::U()
И в потомке перечислить
int U(char){ return РОДИТЕЛЬ::U(char);}
int U(){ return РОДИТЕЛЬ::U();}
...

Сделай по рекомендации - и код скомпилируется.
just for fun

michael
Неотъемлемая часть форума
Сообщения: 434
Зарегистрирован: 12 апр 2004, 11:00
Откуда: г. Владивосток
Контактная информация:

Сообщение michael »

Ага, понял. Значит, в классе B функция U(int) не видна, так как в нём определена собственная функция U(). Единственный выход, как я понимаю, не делать перегрузку. То, что предлагаешь ты, не годится, так как теряется смысл виртуальности. Программа компилируется, но выдаёт неверный результат. Если же функции класса A чисто виртуальные, то код вообще не компилируется.

slavaz
Фанатеющий
Сообщения: 155
Зарегистрирован: 20 июл 2005, 15:22
Откуда: Brest
Контактная информация:

Сообщение 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
Что и требовалось доказать
just for fun

michael
Неотъемлемая часть форума
Сообщения: 434
Зарегистрирован: 12 апр 2004, 11:00
Откуда: г. Владивосток
Контактная информация:

Сообщение michael »

Это действительно работает. Спасибо!

Ответить