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


Автор: knut 20.4.2007, 15:21
добрый день
вот вопрос у меня такой(не совсем извращенныйsmile)
реально ли такое. 
функцию-член присвоить указатель на виртуальный метод предка, а потом вызвать её на экземпляре потомка
Можно ли ли так вообще делать и если не то почему?
ну что то примерно такое

Код


class cBase
{
public:
  virtual void f() { };
 };

class cBase : public cDer
{
public:
  virtual void f() {};
 };

typedef void (cBase::*VirtualFunctionPointer) ();


  cBase a;
  cDer  b;
  cDer* pb = &b;

  VirtualFunctionPointer p = &cBase::f;

  (pb->*p) ();

  p = &cBase::g;
  (pb->*p) ();


Автор: HappyLife 20.4.2007, 15:47
Я не догнал чего ты хочешь.
Тем более не понимаю к чему ты делаешь два класса cBase и что за &cBase::g; вот это вот Г.

Автор: Vyacheslav 20.4.2007, 16:01
Я тоже не понял. Особенно
насчет
Код

class cBase {
class cBase : public cDer {


но могу сказать
что 
Код

typedef void (cBase::*BaseVirtualFunctionPointer) ();
typedef void (cDerived::*DerivedVirtualFunctionPointer) ();

это своершенно разные типы и указатель на метод производного класса тебе не удастся присвоить указателю на метод базового класса. К сожалению :(

Добавлено через 7 минут и 9 секунд
Дополнительно пример из хелпа по С++Builder, с помощью которого поясняется, зачем разработчики ввели ключевое слово __closure
Код

class base

{
  public:
    void func(int x) { };
};
typedef void (base::* pBaseMember)(int);

class derived: public base

{
  public:
    void new_func(int i) { };
};

int main(int argc, char* argv[])
{
  derived         derivedObject;
  pBaseMember m = &derived::new_func; // ILLEGAL
  return 0;
}



Автор: knut 20.4.2007, 16:14
мне надо зделать если возможно такое т.е
указателю на функцию-член присвоить указатель на виртуальный метод предка, а потом вызвать её на экземпляре потомка

Автор: Anikmar 20.4.2007, 17:16
А можно поподробнее саму задачу описать?
Т.е. в наследнике у нас есть указатель. Ему присваиваем адрес метода базового класса. Потом наследник вызывает этот метод через этот указатель.
Вызывается метод базового класса, а так как он виртуальный - то по переданному ему адресу объекта потомка он вызывает необходимую функцию потомка. Правильно я понял?

Автор: Vyacheslav 20.4.2007, 17:18
И никак иначе? Тяжело с Вами.
Я понимаю, что торг не уместен, но может это спасет отца русской демократии smile
Код

 cDer  b;
 b.cBase::f();

 сDer* pb = &b
 b->cBase::f();

Автор: Earnest 20.4.2007, 20:34
Цитата(knut @  20.4.2007,  17:14 Найти цитируемый пост)
мне надо зделать если возможно такое т.е
указателю на функцию-член присвоить указатель на виртуальный метод предка, а потом вызвать её на экземпляре потомка

Мне кажется, это зависит от компилятора. Если он присваивает указателю адрес конкретной функции (предка), то фиг вам. А если адрес специального стабика, где выполняется переадресация, то запросто. Не в курсе, что написано в стандарте. Знаю только, что MSVC это делает. Т.е. указатель, которому присвоен "адрес" виртуальной функции предка, при подстановке this потомка вызовет правильную функцию. А вот старый борланд (еще досовских времен) - нет. Что борланд делает сейчас - не знаю.

Автор: Anikmar 20.4.2007, 21:07
Цитата(Earnest @  20.4.2007,  20:34 Найти цитируемый пост)
Мне кажется, это зависит от компилятора. Если он присваивает указателю адрес конкретной функции (предка), то фиг вам. А если адрес специального стабика, где выполняется переадресация, то запросто. Не в курсе, что написано в стандарте. Знаю только, что MSVC это делает. Т.е. указатель, которому присвоен "адрес" виртуальной функции предка, при подстановке this потомка вызовет правильную функцию. А вот старый борланд (еще досовских времен) - нет. Что борланд делает сейчас - не знаю.

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

Автор: Earnest 20.4.2007, 21:20
Сам подумай, что такое адрес функции-члена? Это просто адрес. Откуда при вызове по этому адресу возьмется информация о полиморфизме? 
Когда компилятор видит в коде вызов виртуальной функции, он не подставлет ее адрес непосредственно, а делает промежуточную переадресацию: из объекта выковыривается адрес vtable, из vtable по номеру берем адрес реально-вызываемой функции. Вот тебе и полиморфизм. А если мы присваиваем указателю адрес, то адрес чего?

MSVC выкручивается просто: и в первом и во втором случае (т.е. и при вызове виртуальной функции, и при использовании ее адреса) генерит небольшой стаб, где выполняет вышеописанную переадресацию. А в качестве адреса выдает адрес этого самого стаба. В результате получается, что как бы ты не использовал  виртуальную функцию, реально ты получаешь адрес стаба. Именно поэтому все всегда виртуально работает.


Автор: Anikmar 20.4.2007, 21:35
Earnest, Да, конечно, я ступил. Я подумал об адресе объекта, а не об адресе метода. 

Бывает... 

Век живи - век учись.
И все равно дураком помрешь...

Автор: knut 21.4.2007, 14:52
Цитата

Мне кажется, это зависит от компилятора. Если он присваивает указателю адрес конкретной функции (предка), то фиг вам. А если адрес специального стабика, где выполняется переадресация, то запросто. Не в курсе, что написано в стандарте. Знаю только, что MSVC это делает. Т.е. указатель, которому присвоен "адрес" виртуальной функции предка, при подстановке this потомка вызовет правильную функцию. А вот старый борланд (еще досовских времен) - нет. Что борланд делает сейчас - не знаю.

Vyacheslav, 
Цитата

это своершенно разные типы и указатель на метод производного класса тебе не удастся присвоить указателю на метод базового класса. К сожалению :(

Так работает smile
Код

class A
   {
   virtual void f ()  = 0;
   };

class B : public A
   {
public:
   void f ()
      {
      std::cout << "B\n";
      }
   };

class C : public A
   {
public:
   void f ()
      {
      std::cout << "C\n";
      }
   };
//  и собственно и вызовы функции

int main()
{

 typedef void (A::*VirtualFunctionPointer) ();
   VirtualFunctionPointer pmfn = &A::f;

   B b;
   C c;
   (b.*pmfn) ();
   (c.*pmfn) ();
return 0;
}




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