Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Вызов указателя на виртуальную функцию, Правильный способ 
:(
    Опции темы
Proxin
Дата 11.10.2015, 19:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 363
Регистрация: 21.6.2008

Репутация: нет
Всего: 3



Здравствуйте, есть следующий класс

Код

class A
{
public:
...
virtual void*   geta(void);
...
}

typedef  void*   ( A::*voidfunct )(void);


и нужно вызвать виртуальную функцию по указателю, который сохранятся вот так:
Код

voidfunct  func = &A::geta;


как вызвать func из объекта obj ? вот так: 
Код


A* obj = new A();
voidfunct  func = &A::geta;

(obj->*func)();


выдаёт acess violation. как получить доступ к действительному адресу функции? если объявить функцию без virtual, вышеприведённый код работает без проблем.


Это сообщение отредактировал(а) Proxin - 12.10.2015, 02:35
PM MAIL   Вверх
feodorv
Дата 12.10.2015, 01:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 11
Всего: 45



Цитата(Proxin @  11.10.2015,  19:49 Найти цитируемый пост)
obj->*func();

А что это? Может, так:
Код
(obj->*func)();



--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
Proxin
Дата 12.10.2015, 02:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 363
Регистрация: 21.6.2008

Репутация: нет
Всего: 3



Точно, ошибся. У меня в коде так как у вас, тут по памяти писал, но ошибка с волайшоном всё равно присутствует.
PM MAIL   Вверх
feodorv
Дата 12.10.2015, 02:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 11
Всего: 45



А что у Вас за компилятор?


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
Proxin
Дата 12.10.2015, 07:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 363
Регистрация: 21.6.2008

Репутация: нет
Всего: 3



vc++ 6.0
PM MAIL   Вверх
math64
Дата 12.10.2015, 09:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 8
Всего: 72



А есть ли по настоящему необходимость в указателе на вирутальную функцию?
Я в своё время посмотрел на ассемблерный код, генерируемый в таких случаях C++Builder (код правильный, рассматривает все мозможые случаи вызова, но один вызов может занять килобайт кода) и делаю обычно так:
Код

class A
{
public:
...
virtual void*   geta(void);
static void* static_geta(A* a) { return a->geta(); }
...
}
typedef  void*   Afunc(A*);

A* obj = new A();
Afunc  func = A::static_geta;
func(obj);
 
PM   Вверх
Proxin
Дата 12.10.2015, 09:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 363
Регистрация: 21.6.2008

Репутация: нет
Всего: 3



math64, можно попробовать и Ваш способ, но меня интересует, как вызвать именно виртуальную функцию, без хаков. Или это невозможно?
PM MAIL   Вверх
math64
Дата 12.10.2015, 11:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 8
Всего: 72



Вероятно, в Microsoft в VC++6.0 решили запретить указатели на виртуальные функции, чтобы не генерить при какждом использовании указателя на функцию класса килобайтный код (заранее не известно - виртульная она или нет, при каждом вызове нужно проверять). Возможно, в более новых компиляторах ситуация другая.
В C++Builder, как альтернатива, есть 8 байтный указатель на функцию, включающий в себя указатель на класс и обычный указатель на функцию (при этом при вызове проверять на вируальность не надо), но это нестандартное расширение языка.

Добавлено через 7 минут и 41 секунду
Вот ссылка на MSDN:
https://msdn.microsoft.com/en-us/library/f2wbycwh.aspx
Код

// virtual_functions.cpp
// compile with: /EHsc  - компилировать с ключом  /EHsc
#include <iostream>
using namespace std;

class Base
{
public:
virtual void Print();
};
void (Base ::* bfnPrint)() = &Base :: Print;
void Base :: Print()
{
cout << "Print function for class Base\n";
}

class Derived : public Base
{
public:
void Print();  // Print is still a virtual function.
};

void Derived :: Print()
{
cout << "Print function for class Derived\n";
}

int main()
{
    Base   *bPtr;
    Base    bObject;
Derived dObject;
bPtr = &bObject;    // Set pointer to address of bObject.
(bPtr->*bfnPrint)();
bPtr = &dObject;    // Set pointer to address of dObject.
(bPtr->*bfnPrint)();
}

//Output: Print function for class Base
Print function for class Derived

PM   Вверх
feodorv
Дата 12.10.2015, 13:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 11
Всего: 45



Цитата(Proxin @  12.10.2015,  07:56 Найти цитируемый пост)
vc++ 6.0 

Попробовал на нём аналогичный код без всяких ключей компиляции:
Код
#include <iostream>

class A {
public:
    virtual void f() { std::cout << "A" << std::endl; }
};

typedef void (A::*vf)();

int main()
{
    A* a = new A();
    vf ff = &A::f;
    (a->*ff)();
    return 0;
}
Он работает.


Цитата(math64 @  12.10.2015,  11:00 Найти цитируемый пост)
компилировать с ключом  /EHsc

Возможно, какой-то ключ компиляции и вызывает проблемы с acess violation.


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
volatile
Дата 12.10.2015, 13:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



Цитата(Proxin @  12.10.2015,  07:56 Найти цитируемый пост)
vc++ 6.0 

у меня в VC 6.0 всё работает.

Код

class A
{
public:
   virtual void* geta ()
   {
      return "base";
   }
};

class B : public A
{
public:
   virtual void* geta ()
   {
      return "derive";
   }
};

typedef void* (A::*voidfunct) ();
voidfunct  func = &A::geta;

int _main ()
{
   A* ob1 = new A();
   A* ob2 = new B();
   printf ("%s\n", (ob1->*func)());
   printf ("%s\n", (ob2->*func)());
   return 0;
}


Код

base
derive


Добавлено через 48 секунд
когда писал не видел последний ответ от feodorv
PM MAIL   Вверх
Proxin
Дата 12.10.2015, 20:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 363
Регистрация: 21.6.2008

Репутация: нет
Всего: 3



А может ли ошибка возникать из-за того, что это всё находится в библиотеке, которая подключается динамически через собственный загрузчик ( впрочем, через LoadLibrary тоже глюк присутствует)?
PM MAIL   Вверх
math64
Дата 13.10.2015, 09:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 8
Всего: 72



Указатель на функцию передаётся в библиотеку?
Если библиотека и основная программа откомпилирована с разными ключами, влияющими на  применение указателей на виртульный метод класса - то да. И не важно, как грузится библиотека - обычной линковкой или через LoadLibrary. Формат указателя если разрешены указатели на виртульный метод класса или нет - разные.
Да, ещё класс нужно объявлять типа class __declspec(dllimport/dllexport) A { ... };
PM   Вверх
Proxin
Дата 15.10.2015, 16:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 363
Регистрация: 21.6.2008

Репутация: нет
Всего: 3



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

typedef void* ( __cdecl A::*cfnc ) ( ... );

class A
{
 //...
 virtual void* __cdecl a(int v);
 //...
}

A* cA;

void callSome(int g)
{
   cA = new A(); 
   cfnc    vfnc = &A::a;
  (cA->*vfnc)(g);
}

выдаёт ошибку о неправильном стёке ( The value of ESP was not properly saved across a function call. This is usually a result of calling a function pointer declared with a different calling convention. )

а вот так:
Код

typedef void* ( A::*cfnc ) ( int );

class A
{
 //...
 virtual void* a(int v);
 //...
}

A* cA;

void callSome(int g)
{
   cA = new A(); 
   cfnc    vfnc = &A::a;
  (cA->*vfnc)(g);
}


всё работает, но мне нужно, чтобы в указатель типа cfnc можно было сохранять и вызывать функции с любым числом любых параметров.

Это сообщение отредактировал(а) Proxin - 15.10.2015, 17:53
PM MAIL   Вверх
feodorv
Дата 16.10.2015, 00:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 11
Всего: 45



Цитата(Proxin @  15.10.2015,  16:22 Найти цитируемый пост)
вызывать функции с любым числом любых параметров

Попробовал, работает:
Код

#include <iostream>
#include <stdarg.h>

class A {
  public:
    virtual int f( char *str, ...);
};

int A::f( char *str, ...)
{
  int count = 0;
  va_list ap;
  va_start( ap, str);

  while( str != NULL )
  {
    std::cout << "A::" << str << std::endl;
    count++;
    str = va_arg( ap, char *);
  }

  return count;
}

typedef int (A::*vf)( char *str, ...);

int main()
{
  A* a = new A();
  vf ff = &A::f;
  std::cout << (a->*ff)( "aaa", "bbb", NULL) << std::endl;
  return 0;
}


Добавлено через 2 минуты и 39 секунд
va_end(ap) забыл...


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
Proxin
Дата 16.10.2015, 09:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 363
Регистрация: 21.6.2008

Репутация: нет
Всего: 3



Первый параметр не обязательно будет int или char*, он может быть вообще любым ( например, __int64, который 8 вместо 4, или char, который вообще 1 )
А из-за чего у меня возникает ошибка при cdecl и функции с воообще не определённым числом параметров? Насколько помню, по спецификации typedef-ы с ( ... ) без параметров разрешены, лишь бы было соглашение о вызове правильное. Там же передача всё равно с конца стёка идёт. По крайней пере с обычными функциями, без классов
Код

typedef void* (__cdecl *pF) ( ... );

void* __cdecl  sample( int a )
{
    printf("%i\n",a);
    return NULL;
}

void main()
{
  pF   a =  &sample;
  (*a)(1);
}

работает же, или я чего-то фундаментального не понимаю?

Это сообщение отредактировал(а) Proxin - 16.10.2015, 10:03
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0919 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.