Каким образом выбирается версия функции, принимающей указатель на полиморфный класс?

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

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

class A
{
 public:
  virtual void m()=0;
};

class B : public A
{
 public:
  virtual void m()
  {
  }
};

class D : public B
{
 public:
  virtual void m()
  {
  }
};

class C : public A
{
 public:
  virtual void m()
  {
  }
};

class E : public C
{
 public:
  virtual void m()
  {
  }
};

void f(A *a)
{
}

void f(B *b)
{
}

void f(C *c)
{
}

int main ()
{
 A *p;
 p=new B;
 f(p);
 delete p;
 p=new C;
 f(p);
 delete p;
 p=new D;
 f(p);
 delete p;
 p=new E;
 f(p);
 delete p;
 return 0;
}
Каким образом здесь выбирается версия функции в каждом случае вызова?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Я три раза перечитал твой пост, поправил там кучу опечаток в коде и самом топике, и, в конце-концов, пришёл к выводу, что тебя расстраивает тишина на форуме, и ты подобными темами просто пытаешься расшевелить людей, ответ же на вопрос тебе совсем не интересует. По другому я не могу объяснить такую кучу опечаток. Это просто наплевательское отношение к читателям. Неужели трудно хотя бы один раз перечитать то, что запостил? Или это сделано намеренно, и это такой грубый троллинг?

По теме: в данном случае функция будет выбрана по наибольшему соответствию типа. Иными словами, во всех случаях будет вызвана f(A*).

А в чём смысл таких функций, если полиморфизм будет работать благодаря виртуальным методам? Как раз одной функции достаточно.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Компилятор ни одной очепятки не заметил.
По теме: в данном случае функция будет выбрана по по наибольшему соответствию типа. Иными словами, во всех случаях будет вызвана f(A*).
Страуструп прямо пишет, что выбирается функция, максимально соответствующая фактическому типу. Если есть версия, принимающая фактический тип, то вызывается она, иначе - версия, принимающая указатель на ближайшего предка. В данном случае

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

class A
{
 public:
  virtual void m()=0;
};

class B : public A
{
 public:
  virtual void m()
  {
  }
};

class D : public B
{
 public:
  virtual void m()
  {
  }
};

class E : public C
{
 public:
  virtual void m()
  {
  }
};

void f(A *a)
{
}

void f(B *b)
{
}

void f(C *c)
{
}

int main ()
{
 A *p;
 p=new B;
 f(p); //Вызывается версия, принимающая B* 
 delete p;
 p=new C; 
 f(p); //Вызывается версия, принимающая C* 
 delete p;
 p=new D; 
 f(p); //Вызывается версия, принимающая B* 
 delete p;
 p=new E;
 f(p); //Вызывается версия, принимающая C* 
 delete p;
 return 0;
}
. А вот каким образом это делается? Если

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

class A
{
 public:
  virtual void m()=0;
};

class B : public A
{
 public:
  virtual void m()
  {
  }
};

class D : public B
{
 public:
  virtual void m()
  {
  }
};

class E : public C
{
 public:
  virtual void m()
  {
  }
};

void f(B b)
{
}

void f(C c)
{
}

int main ()
{
 B b;
 C c;
 D d;
 E e;
 f(b);
 f(c);
 f(d);
 f(e);
 return 0;
}
, то фактический тип известен на этапе компиляции. А в случае указателя?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Вместо public там было везде написано publuc. Вместо virtual - firtual. Интересно, как это компилилось. Может компилятор специальный? :)
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

пришёл к выводу, что тебя расстраивает тишина на форуме, и ты подобными темами просто пытаешься расшевелить людей, ответ же на вопрос тебе совсем не интересует.
Ну не знаю, если погуглить "taras atavin", можно много чего интересного найти.
2B OR NOT(2B) = FF
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Absurd писал(а):Ну не знаю, если погуглить "taras atavin", можно много чего интересного найти.
Первая же ссылка ведёт на некий форумный профиль с такой же авой, как и у нашего друга. Это что, один и тот же человек? :)
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

А в чём смысл таких функций, если полиморфизм будет работать благодаря виртуальным методам? Как раз одной функции достаточно.
А ни кто не говорил, что эти функции вообще будут обращаться к методам. Может они обращаются только к переменным-членам?

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

class A
{
 public:
  virtual void m()=0;
};

class B : public A
{
 public:
  int x;
  virtual void m()
  {
  }
};

class C : public A
{
 public:
  double y;
  virtual void m()
  {
  }
};

class D : public B
{
 public:
  char c;
  virtual void m()
  {
  }
};

class E : public C
{
 public:
 bool f 
  virtual void m()
  {
  }
};

void f(A *a)
{

}

void f(B *b)
{
 x=2; 
 if (dynamic_cast<D*>b)
 {
  (dynamic_cast<B*>a)->с='f';
 }
}

void f(C *c)
{
 y*=2.4;
 if (dynamic_cast<D*>b)
 {
  (dynamic_cast<B*>a)->f=!(dynamic_cast<B*>a)->f;
 }
}

int main ()
{
 A *p;
 p=new B;
 f(p);
 delete p;
 p=new C;
 f(p);
 delete p;
 p=new D;
 f(p);
 delete p;
 p=new E;
 f(p);
 delete p;
 return 0;
}
.
Вместо public там было везде написано publuc. Вместо virtual - firtual. Интересно, как это компилилось. Может компилятор специальный?
А куда при исправлении делся класс C?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Первая же ссылка ведёт на некий форумный профиль с такой же авой, как и у нашего друга.
Я случайно напоролся на этот профиль когда значение "Эй Си" искал.
2B OR NOT(2B) = FF
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Сионист, так код не пишут: касты не для этого созданы.

По поводу указателя - я уже сказал, как всё будет работать. Всегда будет вызываться f(A*), так как ты сам делаешь кастинг к указателю этого типа при присваивании.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Объекты могут поставляться сторонним кодом, при этом может отсутствовать возможность переписать методы, но могут требоваться различные действия в зависимости от фактического класса каждого объекта. Предположим, под A скрывается TEvent, версии функции - это обработчики, а метод вызывается в том случае, если приложение обрабатывает данное событие. Факт наличия версии, принимающей указатель на фактический класс, - не достаточный признак. Пусть

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

TEvent
{
 private:
  volatile bool f;
 public :
  virtaual void Processed()=0;
};
class TCharEvent: public TEvent
{
};
class TUnichar32Event: public TCharEvent
{
 public:
   public:
 virtaual void Processed()
 {
  f=true;
 }
 chart32_t Code;
};
class TUnichar16Event: public TCharEvent
{
 public:
   public:
 virtaual void Processed()
 {
  f=true;
 }
 union
 {
  wchar_t Code;
  wchar_t SurrogatCode[2];
 };
};
class TAnsicharEvent: public TCharEvent
{
 public:
   public:
 virtaual void Processed()
 {
  f=true;
 }
 char Code;
};
class TLButtonEvent: public TEvent
{
 public:
   public:
 virtaual void Processed()
 {
  f=true;
 }
 int x;
 int y;
};
и по каким то причинам решено TUncar32Event и TAnsicharEvent обрабатывать в одном обработчике, принимающем указатель на TCharEvent, TLButttonEvent - в другой, а TUnichar16Event - не обрабатывать вообще. Хорошо, когда вызываемый и вызывающий код прописаны в одном проекте, тогда просто используем указатель в качестве фактического параметра и ни каких гвоздёв. А если они в разных проектах? Если я передам один указатель на функцию, то он будет указывать только на одну версию и тест показывает, что по указателю на функцию вызывается та версия, на которую он указывает, всегда одна и та же. Тот же тест показывает, что при вызове функции по имени самой функции стандарт соблюдается и вызываемая версия зависит от фактического класса объекта, на которую указывает параметр. А как это достигается? Может отсюда можно вытянуть способ выбора версии в стороннем вызывающем коде?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Ответить