Автор
|
Тема: Указатель на метод другого класса
|
DmitryRyvkin |
опубликован 18-10-2001 07:10 MSK
Я новичок в C++, не пинайте сильно,плиз. Вопрос такой. Допустим, в классе А есть переменная указывающая на некий метод другого класса void (CAnotherClass::* funcAddr)(void). Тут все ясно. Но как быть, если я еще не знаю, в КАКОМ именно классе содержится метод ? В Паскале, как я слышал есть procedure .... of object. А в С++ ? Мне удалось сделать это, но пришлось вводить некий фиктивный класс, нужный, фактически, для приведения типа, и еще понадобилась новая структура, хранящая адрес ф-ции + this на конкретный объект при присвоении. (ведь и в Паскале эта конструкция 8 байт, а не 4) Может , я чего-то не понял ? Спасибо.
|
Flex Ferrum
|
опубликован 18-10-2001 10:41 MSK
1. Опиши по подробнее задачу, которую ты хочешь решить. 2. Может быть ты описался, и тебе не известно, для какого _объекта_ необходимл применить метод? Если так, то косвенный вызов функции в С++ (для объекта) делается с помощью оператор ->* (если объект передан по указателю), или .*, если объект передан по ссылке. Слева от оператора должен стоять объект, а справа - указатель на функцию. Например: this ->* FunctPtr(a,b,c); или obj .* FunctPtr(a,b,c); |
server_mouse
|
опубликован 18-10-2001 11:20 MSK
Если я правильно понял, то тебя спасёт абстрактный класс. Все классы на которые нужно ссылаться наследуешь от него, а в классе использующем метод тип приводишь к абстрактному. Если будешь по ссылке(указателю) на абстрактный удалять наследника, делай деструктор виртуальным. |
DmitryRyvkin
|
опубликован 18-10-2001 00:55 MSK
Лучше в сурцах вопрос перефразирую. 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
Ну так и храни указатель на объект другого класса.{ // 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
Да в том то вся и проблема - по этой схеме получаю - 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
Поясняю также на примерах. В 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
Пургааааа..... Что значит не знаю кому передать... А сообщения виндов на что - генери WM_COMMAND со своими причандлами и щли HWND GetParent(); Делов то. Все остальное - от лукавого! А если твой клиент вообще будет на АПИ писан? |
server_mouse
|
опубликован 18-10-2001 14:29 MSK
>Что значит не знаю кому передать...Что-то мне это дюже COM напоминает.... |
the_moon
|
опубликован 18-10-2001 14:48 MSK
Привет,this указывает на виртуальную таблицу твоего класса, и ты ни когда не сможешь этот указатель преоброзовать к тому что тебе надо, тоесть к AnotherClass потому как он вообще хрен знает гле в памяти сидит.Но если ты знаешь, а ты по любому должен знать о методе, чтоб его вызвать, о чужом классе классе, то значит, что ты можешь свой класс унаследовать от AnotherClass и вызывай функцию наздоровье, для того наследование и придумали, это если ты хочешь использовать функциональность AnotherClassa. А если тебе надо вызвать метод объекта, то.сохранить указатель на объект класс,а хоть как void*, и вызывать его методы. Пока |
server_mouse
|
опубликован 18-10-2001 15:13 MSK
И всё таки я в таких случаях делаю так: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
Всем спасибо, особенно 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
Отличаются тем, что к каждому нестатическому методу класса в качестве неявного параметра передается указатель на экземпляр этого класса, к которому применяется метод. В твоем случае в метод 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
Рискну все же надоесть, но скажите пожалуйста, могу ли я приводить типы как в этом примере. 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)(); Извините если достал. |