WWW.ИСХОДНИКИ.РУ cpp.sources.ru
java.sources.ru web.sources.ru soft.sources.ru
jdbc.sources.ru asp.sources.ru api.sources.ru

  Форум на исходниках
  C / C++ / Visual C++
  Указатель на метод другого класса

СПРОСИТЬ  ОТВЕТИТЬ
профайл | регистрация | faq

Автор Тема:   Указатель на метод другого класса
DmitryRyvkin опубликован 18-10-2001 07:10 MSK   Click Here to See the Profile for DmitryRyvkin   Click Here to Email DmitryRyvkin  
Я новичок в C++, не пинайте сильно,плиз. Вопрос такой. Допустим, в классе А
есть переменная указывающая на некий метод другого класса
void (CAnotherClass::* funcAddr)(void). Тут все ясно. Но как быть, если я еще
не знаю, в КАКОМ именно классе содержится метод ? В Паскале, как я слышал
есть procedure .... of object. А в С++ ? Мне удалось сделать это, но пришлось
вводить некий фиктивный класс, нужный, фактически, для приведения типа, и еще
понадобилась новая структура, хранящая адрес ф-ции + this на конкретный
объект при присвоении. (ведь и в Паскале эта конструкция 8 байт, а не 4)
Может , я чего-то не понял ? Спасибо.

Flex Ferrum опубликован 18-10-2001 10:41 MSK     Click Here to See the Profile for Flex Ferrum  Click Here to Email Flex Ferrum     
1. Опиши по подробнее задачу, которую ты хочешь решить.
2. Может быть ты описался, и тебе не известно, для какого _объекта_ необходимл применить метод? Если так, то косвенный вызов функции в С++ (для объекта) делается с помощью оператор ->* (если объект передан по указателю), или .*, если объект передан по ссылке. Слева от оператора должен стоять объект, а справа - указатель на функцию. Например:
this ->* FunctPtr(a,b,c);
или
obj .* FunctPtr(a,b,c);
server_mouse опубликован 18-10-2001 11:20 MSK     Click Here to See the Profile for server_mouse  Click Here to Email server_mouse     
Если я правильно понял, то тебя спасёт абстрактный класс. Все классы на которые нужно ссылаться наследуешь от него, а в классе использующем метод тип приводишь к абстрактному.
Если будешь по ссылке(указателю) на абстрактный удалять наследника, делай деструктор виртуальным.
DmitryRyvkin опубликован 18-10-2001 00:55 MSK     Click Here to See the Profile for DmitryRyvkin  Click Here to Email DmitryRyvkin     
Лучше в сурцах вопрос перефразирую.
class CAnotherClass
{
public:
void DoSomething (void) {}
};
class CSomeClass
{
//typedef
public:
void ExecuteAnotherMethod (void) {
((CAnotherClass *)this->*m_pFuncAnother)();
}
//members
void (CAnotherClass::* m_pFuncAnother)(void) ;
//ptrToAnother ;
};

void main(void)
{
CSomeClass someClass;
CAnotherClass another;
someClass.m_pFuncAnother=another.DoSomething;
someClass.ExecuteAnotherMethod();
}

В классе CSomeClass,выходит, я должен знать о классе CAnotherClass,
для явного приведения типов. Так ведь ? А вот это мне и не нравится.
Если б в языке было ключ. слово типа thiscall, то думаю, было бы проще.
Кроме того, что передается в при вызове ((CAnotherClass *)this->*m_pFuncAnother)()
в метод ? В моем случае - this на someClass. Но нужен то - на another !!!
Иначе не толку от методов, разве что от статических. В Паскале при подобном
присвоении создается пер. 8 байт, 4 байта адреса ф-ции, 4 на this.
Но в C++ похоже, так не выйдет ?
Извините, если надоел, или, мб, чепуху горожу. Сия проблема возникла
при переходе с CBuilder на VC 6, а как в классе сделать переменную на
OnClick ? Т.е указатель на метод другого класса ?

migel опубликован 18-10-2001 13:04 MSK     Click Here to See the Profile for migel  Click Here to Email migel     
Ну так и храни указатель на объект другого класса.

{
//
void ExecuteAnotherMethod (void) {
(m_pAnother->*m_pFuncAnother)();
//
CSomeClass someClass;
CAnotherClass another;
someClass.m_pAnotherClassObj = &another;
someClass.m_pFuncAnother=another.DoSomething;
someClass.ExecuteAnotherMethod();
}

Но вообще то что ты пытаешся сделать изврат.
Должен быть более простой путь - думай над дизайном программы.

DmitryRyvkin опубликован 18-10-2001 13:20 MSK     Click Here to See the Profile for DmitryRyvkin  Click Here to Email DmitryRyvkin     
Да в том то вся и проблема - по этой схеме получаю -
error C2440: 'newline' : cannot convert from 'class CSomeClass *const ' to 'class CAnotherClass *const '
Вот так. Хотя согласен, это бывает нужно редко, но нужно. Ведь принцип ООП -
как можно меньше знать о других классах. А тут без приведения типов не обойтись,
для чего эти знания нужны. А если я ее из dll экспортирую ? Тут дизайн не поможет.
Все таки пока я так и не выяснил как это сделать.
PS виртуальные ф-ции помогут опять же при том что кто является наследником...
А вот это как раз и ни к чему. Нет у меня знаний о чужом классе и все тут.
Я считаю, что C++ более мощный язык чем Pascal(Delphi), но там то это есть!
Неужели подобное никому никогда не было надо ? Представьте класс CMyButton.
У него событие по кликанью. Его надо либо DefWinProc обработать, либо вызвать
метод другого класса (Form1::OnClick). Так как его вызвать, если на стадии
разработки моего класса CMyButton я понятия не имею, что вызывать придется ?
Flex Ferrum опубликован 18-10-2001 13:49 MSK     Click Here to See the Profile for Flex Ferrum  Click Here to Email Flex Ferrum     
Поясняю также на примерах. В Borland C++ Builder специально для реализации этой семантики ввели специальный спецификатор вызова - __closure. В документации про него написано, что "The __closure keyword is used to declare a special type of pointer to a member function. Unlike a regular C++ member function pointer, a closure contains an object pointer.".
То есть, когда у тебя Делфи или Билдер начинает создавать форму, она неявно присваивает обработчикам событий объект и указатель на метод, которые эти события будут обрабатывать. В остальных версиях C++ такого нет. По этому, для реализации подобного интерфейса придумали так называемую карту событий. А именно: у тебя объект содержит массив структур, в простейшем случае содержащих тип сабытия (например, WM_LBUTTONDOWN) и указатель на обрабатывающий это событие метод:
TEventStruct TMyClass::Events[] =
{
{WM_LBUTTONDOWN, &TMyClass :: OnClick},
// ...
{0,0} // Конец массива
};

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

TEventHander TMyClass::FindHandler(int EventType)
{
TEventHandler ret_val = NULL;
for(...)
//... реализация поиска события

return ret_val ? ret_val : TBaseClass::FindHandler(EventType);
}

Все это требует некоей диспетчерской функции (статической), в которую попадают все приходящие события, и она выбирает, какой объект может их обработать:

LRESULT TEventDispatcher::DispatchEvent(HWND hWnd, UINT MessageId, WPARAM wParam, LPARAM lParam)
{
TEventReceiver* receiver = FindWindow(hWnd);
bool handled = false;

if (receiver)
{
TEventHandler* handler = receiver -> FindHandler(MessageId);
if (handler)
return receiver ->* handler(MessageId, wParam, lParam);
}
if (!handled)
return DefWindowProc(hWnd, MessageId, wParam, lParam);
}

Все это требует гомогенной иерархии (т.е. чтобы все потенциальные обработчики росли из одного корня, в моем случае из TEventHandler).
Но вся фишка в том, что это уже реализовано в MFC. Вот.

migel опубликован 18-10-2001 14:08 MSK     Click Here to See the Profile for migel  Click Here to Email migel     
Пургааааа.....
Что значит не знаю кому передать...
А сообщения виндов на что - генери WM_COMMAND со своими причандлами и щли HWND GetParent();
Делов то.
Все остальное - от лукавого! А если твой клиент вообще будет на АПИ писан?
server_mouse опубликован 18-10-2001 14:29 MSK     Click Here to See the Profile for server_mouse  Click Here to Email server_mouse     
>Что значит не знаю кому передать...

Что-то мне это дюже COM напоминает....

the_moon опубликован 18-10-2001 14:48 MSK     Click Here to See the Profile for the_moon  Click Here to Email the_moon     
Привет,

this указывает на виртуальную таблицу твоего класса, и ты ни когда не сможешь этот указатель преоброзовать к тому что тебе надо, тоесть к AnotherClass потому как он вообще хрен знает гле в памяти сидит.Но если ты знаешь, а ты по любому должен знать о методе, чтоб его вызвать, о чужом классе классе, то значит, что ты можешь свой класс унаследовать от AnotherClass и вызывай функцию наздоровье, для того наследование и придумали, это если ты хочешь использовать функциональность AnotherClassa. А если тебе надо вызвать метод объекта, то.сохранить указатель на объект класс,а хоть как void*, и вызывать его методы.

Пока

server_mouse опубликован 18-10-2001 15:13 MSK     Click Here to See the Profile for server_mouse  Click Here to Email server_mouse     
И всё таки я в таких случаях делаю так:

class CInterface
{
Interface()=0; //Описываем чего и как передаётся...
};

class One : public CSomeClass,CInterface
{//......
};

class Two : public COtherSomeClass,CInterface
{//......
};

И вот теперь тебе абсолютно всё равно что у тебя есть: указатель на One или на Two -- приводиш всех к типу CInterface и приспокойно вызываешь Interface().
К тому же обязательность реализации у наследников (ты же будешь делать экземпляры?)Interface() на мой взгляд большой плюс. Ничего не забудешь, ничего не упустишь.

Хотя конечно ты должен знать хотя бы CInterface. А то как-то -- хочу того незнаю чего...

DmitryRyvkin опубликован 18-10-2001 17:38 MSK     Click Here to See the Profile for DmitryRyvkin  Click Here to Email DmitryRyvkin     
Всем спасибо, особенно Flex Ferrum, именно _closure я и имел ввиду, только
с Билдером мало возился, толком не понял тогда.
А вот к Moon вопрос.
"и ты ни когда не сможешь этот указатель преоброзовать к тому что тебе надо, тоесть к AnotherClass
потому как он вообще хрен знает гле в памяти сидит" Чем void func (void)
в одном классе отличается от такой же , но в другом классе ? Разве у них
параметры в стек по разному толкаются или результат (если есть) по разному
возвращается ? Почему нельзя привести ? (Собственно привести можно, и VC это делает,может что не так ?)Нет ведь, и _closure - тому пример.
Сам я пишу на MFC и там в итоге выходят конструкции типа
(pTarget->*mmf.pfn_COMMAND)(); , где mmf -
union типа
union MessageMapFunctions
{
AFX_PMSG pfn;
void (AFX_MSG_CALL CCmdTarget::*pfn_COMMAND)();
...............................................
и так все далее все методы что только придумать можно
};
Ну и все классы, понятно, от CCmdTarget производны.
Просто думал, можно ли проще. Похоже, нет.
Еще раз всем спасибо.

Flex Ferrum опубликован 18-10-2001 18:47 MSK     Click Here to See the Profile for Flex Ferrum  Click Here to Email Flex Ferrum     
Отличаются тем, что к каждому нестатическому методу класса в качестве неявного параметра передается указатель на экземпляр этого класса, к которому применяется метод. В твоем случае в метод func бедт таки передан один парметр - указатель на экземпляр. Ну а то, что указатели на экземпляры классов SomeClass и AnotherClass будут разными, это и так понятно. Отсюда вывод, что если делать приведение типов явно в обход контроля типов компилятором, то можно такого наворотить, что потом очень и очень долго будешь выяснять, откуда же у тебя AccessVolation лезут. Кстати, модификатор __closure можно сэмулировать на стандартном С++ примерно следующим способом (сочиняю на ходу):

template <class T> struct __closure
{
T* object;
void (T::*funct)(void);

__closure(T* obj, void (T::*_funct)())
{
object = obj;
funct = _funct;
}
void operator ()() {return obj ->* funct();}
};

DmitryRyvkin опубликован 19-10-2001 04:19 MSK     Click Here to See the Profile for DmitryRyvkin  Click Here to Email DmitryRyvkin     
Рискну все же надоесть, но скажите пожалуйста, могу ли я приводить типы как в этом примере.
class CFictiveClass{};
typedef void (CFictiveClass::*ptrToFictVoid)(void);
class A
{
public:
ptrToFictVoid m_ptr;
void Execute(void)
{
(((CFictiveClass*)this)->*m_ptr)();
};
};
class B
{
public:
void DoSomething (void) {printf("Do something\n");}
};

void main(void)
{
A a;
a.m_ptr=(ptrToFictVoid) B::DoSomething;
a.Execute();
}
Собственно интересует правомочность
(((CFictiveClass*)this)->*m_ptr)();
Извините если достал.

СПРОСИТЬ  ОТВЕТИТЬ
Перейти:


E-mail | WWW.ИСХОДНИКИ.RU

Powered by: Ultimate Bulletin Board, Freeware Version 5.10a
Purchase our Licensed Version- which adds many more features!
© Infopop Corporation (formerly Madrona Park, Inc.), 1998 - 2000.