Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Перегрузка виртуальных функций


Автор: gamedeveloper 2.6.2008, 22:29
Можно ли на С++ перегрузить виртуальную функцию в классе наследнике?

Код не рабочий, но думаю идея ясна.
Код

class SuperClass {
public:
        virtual void memfunc(char *str)=0;
};

class SubClass : SuperClass {
public:
        void memfunc(double d) {
                
        }
}

Автор: Fazil6 2.6.2008, 22:45
ну в общем случае можно, только спрашивается для чего?

Автор: Lazin 2.6.2008, 22:47
Цитата(gamedeveloper @  2.6.2008,  22:29 Найти цитируемый пост)
думаю идея ясна

никак нет, абсолютно не ясна smile
вот это
Код

void memfunc(double d)

уже не виртуальная фунция, к тому-же SuperClass о ней ничего не знает, так что я затрудняюсь ответить как это может быть использовано...

Автор: gamedeveloper 2.6.2008, 23:01
Если идея не ясна, тогда так:

Есть базовый клас SuperClass. В нём есть виртуальная функция memfunc с входным параметром типа char*.
Есть наследник базового класса SubClass. В нём есть унаследованная виртуальная функция memfunc, НО уже с входным параметром double.
Такое может быть на Си++?

То есть можно ли перегрузить при наследовании виртуальную функцию?

Автор: baldina 2.6.2008, 23:07
нельзя. и не нужно:
Код

SuperClass *p = new SubClass;

p->memfunc (1.0); // error: нет функции SuperClass::memfunc (double);
p->memfunc ("text"); // что должно быть вызвано?

у тебя явно какая-то задача, имеющая другое (возможно, очень простое) решение

Автор: gamedeveloper 2.6.2008, 23:18
Задачи как таковой нет. Просто с другом о языках спорим. Вот он и завёл тему про перегрузку виртуальных функций в иерархии. Я пробовал сделать, но компилятор и линкер по очереди ругались, то нельзя создать экземпляр абстрактного класса, то в SuperClass нет void memfunc(double).
Вот и решил у более обознаных людей спросить.

Я пока тему не снимаю, может у кого то ещё есть мнение...

Автор: opjox 3.6.2008, 01:08
Можно перегрузить. Однако, чтобы создать экземпляр SubClass надо в нем все таки определить memfunc(char *str). Также в предложенном коде есть неточность – функция memfunc(double d) не будет виртуальной, это можно наглядно понять из этого кода (этот код аналогичен первичному, за исключением необходимо определенной memfunc(char *str) ):

Код

#include <iostream>

class A {
public:
  virtual void memfunc(char *str) = 0;
};

class B: public A {
public:
  void memfunc(double d)  // это не виртуальная функция
  {
    std::cout << "memfunc(double d): B" << std::endl;
  }

  void memfunc(char *str) // чтобы создать экземпляр этого класса, надо определить эту функцию
  {
    std::cout << "memfunc(char *str): B" << std::endl;
  }
};

class C: public B {
public:
  void memfunc(char *str)
  {
    std::cout << "memfunc(char *str): C" << std::endl;
  }

  void memfunc(double d)
  {
    std::cout << "memfunc(double d): C" << std::endl;
  }
};

int main()   
{  
  A *a;
  B  b;
  C  c;
  
  a = &b;
  a->memfunc(0);

  a = &c;
  a->memfunc(0);

  B *p;
  p = &b;
  p->memfunc(0.);
  p = &c;
  p->memfunc(0.); // указатель имеет тип B, поэтому будет вызвана функция B::memfunc(double d)

  return 0;   
}


Предполагаю, что в итоге хотелось увидеть примерно это (вариант перегрузки виртуальной функции в классе-наследнике):

Код

#include <iostream>

class A {
public:
  virtual void memfunc(char *str)
  {
    std::cout << "memfunc(char *str)::A" << std::endl;
  }

  virtual void memfunc(double d)
  {
    std::cout << "memfunc(double d)::A" << std::endl;
  }
};

struct ST {char bb;};

class B: public A {
public:
  virtual void memfunc(ST bb) // перегружаем, ключевое слово virtual здесь необходимо
  {
    std::cout << "memfunc(ST bb)::B" << std::endl;
  }

  void memfunc(double d)      // переопределяем
  {
    std::cout << "memfunc(double d)::B" << std::endl;
  }
};

class C: public B {
public:
  void memfunc(char *str)     // переопределяем
  {
    std::cout << "memfunc(char *str)::C" << std::endl;
  }

  void memfunc(double d)      // переопределяем
  {
    std::cout << "memfunc(double d)::C" << std::endl;
  }

  void memfunc(ST bb)         // переопределяем
  {
    std::cout << "memfunc(ST bb)::C" << std::endl;
  }
};

int main()
{
  A a, *pa;
  B b, *pb;
  C c;
  ST s;

  pa = &a;
  pa->memfunc((char *)0);
  pa->memfunc(0.);

  std::cout << std::endl;

  pa = pb = &b;
  pa->memfunc((char *)0); // она не была переопределена в производном классе
  pa->memfunc(0.);
  pb->memfunc(s);         // pa нельзя использовать, т.к. он ничего не знает о memfunc(ST bb)
  
  std::cout << std::endl;

  pa = pb = &c;
  pa->memfunc((char *)0);
  pa->memfunc(0.);
  pb->memfunc(s);
  return 0;
}


Автор: georain 3.6.2008, 01:46
gamedeveloper, нужно различать статическое и динамическое связывание. Когда виртуальная функция переопределяется, то при её вызове происходит поиск конкретной реализации в зависимости от вызывающего класса (во время выполнения). А когда виртуальная функция перегружается то поиск производится по типу аргументов (во время компиляции) и смысл виртуальности пропадает.

Автор: opjox 3.6.2008, 02:13
georain, или я вас не до конца понял, или же вы немного неправы. Довольно абстрактно говоря, перегрузку виртуальной функции можно назвать созданием еще одной ветви в виртуальной иерархии. 

Прибегая к перегрузке виртуальных функций, мы столкнемся как с динамическим, так и статическим связыванием: во время компиляции будет определена ветвь исходя из типа, а во время исполнения будет выбрана конкретная реализация функции из той ветви, которая была определена на момент компиляции. При этом не теряется смысл виртуальности.



P.S. или мне опять нужно больше спать? smile 

Автор: baldina 3.6.2008, 09:13
georain и opjox правы оба, только говорят чуть о разном. Тут и то и другое - во время компиляции выбирается сигнатура функции, во время выполнения (если данная сигнатура определила виртуальную функцию) происходит поиск функии по таблице.
Все может иметь место одновременно.
Можно представить это таким образом: виртуальная функция определяет набор поведений объектов различных типов в одной иерархии (скажем, функция draw() в иерархии графических примитивов), а перегруженная - набор однотипных операций по различным входным данным (например, функции MessageBox (UINT RscId) и MessageBox (const char* message))

gamedeveloper, возможности языка не возникают на пустом месте. Есть конкретные задачи, для них - конкретные решения в языке. Есть вещи, напрямую языком не поддерживаемые. Например, мультиметоды не поддерживаются С++ - они как раз близки к обсуждаемой теме.

Автор: Alek86 3.6.2008, 09:51
Код

class SuperClass {
public:
 virtual void memfunc(char *str);
};

class SubClass : SuperClass {
public:
 using SuperClass::memfunc;
 void memfunc(double d) {}
};

Автор: georain 3.6.2008, 14:02
Согласен opjox более правильно изложил smile

Автор: gamedeveloper 5.6.2008, 22:40
Всем спасибо.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)