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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Message Map, обработка виндовских сообщений 
:(
    Опции темы
Jcs
Дата 29.9.2003, 20:29 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











Подскажите, пожалуйста, если кто знает, как связать конкретное сообщение (к примеру WM_CREATE) c нестатической функцией-членом класса. Можно, конечно, использовать и статические, выдирая при этом указатель на конкретный экземпляр класса из описателя окна, и вызывая из него нестатическую функцию, но как-то не охота забивать класс парами функций на каждое сообщение. Заранее спасибо.
  Вверх
mr.DUDA
Дата 29.9.2003, 23:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


3D-маньяк
****


Профиль
Группа: Экс. модератор
Сообщений: 8244
Регистрация: 27.7.2003
Где: город-герой Минск

Репутация: 25
Всего: 232



Если програмить на MFC или ATL/WTL, то всё просто - там юзается простой макрос типа BEGIN_MESSAGE_MAP/END_MESSAGE_MAP (или DECLARE_MESSAGE_MAP), внутри него связывается любое сообщение с конкретной нестатической функцией. Или ты про чистый WinAPI ?


--------------------
user posted image
PM MAIL WWW   Вверх
Jcs
Дата 30.9.2003, 03:40 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











Спасибо за ответ, я про WIN API в чистом виде, в самих MFC к месссаджам привязаны нестатические функции, а я сколько ни пытался, даже указатель на ф-цию-член класса (нестатическую) выдрать не могу... Как они так сделали..
  Вверх
RAN
Дата 30.9.2003, 09:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Экс. модератор
Сообщений: 709
Регистрация: 14.3.2003
Где: Щёлково Моск.обл.

Репутация: 5
Всего: 6



Это вопрос ко мне. Я уже где-то отвечал. Я делаю так, как ты написал:
Цитата
выдирая указатель на конкретный экземпляр класса из описателя окна, и вызывая из него нестатическую функцию

Но только я не понял, почему две функиции. Функция одна, и она не статическая. Вот пример класса:

Код

#ifndef WND_H
#define WND_H

#include "windows.h"

class CWnd {
private:
HWND hwnd;
static const char lpszClassName[];
static LRESULT CALLBACK Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
void OnSize(int nWidth, int nHeight);
void OnMove(int nLeft, int nTop);
void OnPaint(HDC dc, LPRECT lpRect);
public:
CWnd(HWND hOwner, int Left, int Top, int Width, int Height);
~CWnd();
static void RegisterClass();
static void UnregisterClass();
inline HWND getHandle() { return hwnd; }
};

#endif //WND_H


Код

const char CWnd::lpszClassName[] = "InventoryCardNavigator";

CWnd::CWnd(HWND hOwner, int Left, int Top, int Width, int Height)
{
   CreateWindowEx(WS_EX_CLIENTEDGE, const_cast<char*>(lpszClassName), NULL, WS_CHILD | WS_VISIBLE,
 Left, Top, Width, Height, hOwner, NULL, hInst, (LPVOID)this);
}

CWnd::~CWnd()
{
DestroyWindow(hwnd);
}

LRESULT CALLBACK CWnd::Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch( uMsg )
{
case WM_CREATE:
 SetWindowLong(hwnd, 0, LONG( LPCREATESTRUCT(lParam)->lpCreateParams ) );
 ((CWnd*) ( LPCREATESTRUCT(lParam)->lpCreateParams ))->OnCreate(hwnd, LPCREATESTRUCT(lParam));
    break;
case WM_PAINT:
 {
  PAINTSTRUCT ps;
  HDC DC = BeginPaint(hwnd, &ps);
  SetBkMode(DC, TRANSPARENT);
  ((CWnd*)GetWindowLong(hwnd, 0))->OnPaint(DC, &ps.rcPaint);
  EndPaint(hwnd, &ps);
 }
 return 0;
case WM_SIZE:
 ((CWnd*)GetWindowLong(hwnd, 0))->OnSize(LOWORD(lParam), HIWORD(lParam));
 break;
case WM_MOVE:
 ((CWnd*)GetWindowLong(hwnd, 0))->OnMove(LOWORD(lParam), HIWORD(lParam));
 break;
}
return( DefWindowProc( hwnd, uMsg, wParam, lParam ) );
}

void CWnd::OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
CWnd::hwnd = hwnd;
}

void CWnd::OnSize(int nWidth, int nHeight)
{

}

void CWnd::OnMove(int nLeft, int nTop)
{

}

void CWnd::OnPaint(HDC dc, LPRECT lpRect)
{

}

void CWnd::RegisterClass()
{
WNDCLASS wcl;
wcl.style = 0;
wcl.lpfnWndProc = CWnd::Proc;
wcl.cbClsExtra = 0;
wcl.cbWndExtra = 4;
wcl.hInstance = hInst;
wcl.hIcon = NULL;
wcl.hCursor = LoadCursor(NULL, IDC_ARROW);
wcl.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcl.lpszMenuName = NULL;
wcl.lpszClassName = lpszClassName;
::RegisterClass(&wcl);
}

void CWnd::UnregisterClass()
{
::UnregisterClass(lpszClassName, hInst);
}



Этот код получился "вырезанием" из реального проекта и исправлением кое-чего. Поэтому возможно что-то не так, но принцип такой.
Особенности использования: приходиться вызывать для каждого класса функции RegisterClass и UnregisterClass. Это возможно обойти. Но мне показалось, что так лучше. А так можно в конструкторе класса проверять зарегистрирован класс и если нет, то вызывать RegisterClass. А unregisterclass вообще не вызывать.

P.S. Кстати, это класс child-окна.
PM MAIL ICQ   Вверх
Jcs
Дата 30.9.2003, 10:27 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











Это действительно работает, только я пытался избежать использования switch в функции обработки окна. Для этого использовал свой класс CMsgMap, в котором каждому конкретному сообщению сопоствляется своя функция обработки.. Может это, конечно, требуется не всем, но мне в проекте надо функцию обработки генерить на лету, в зависимости от состояния приложения. Но похоже все-таки придется использовать статические функции, передавая им указатель на экземпляр класса. Как выяснилось, нестатические тоже его получают, только при помощи компилятора, так что разницы особой не будет. Спасибо за отклик.
  Вверх
RAN
Дата 30.9.2003, 14:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Экс. модератор
Сообщений: 709
Регистрация: 14.3.2003
Где: Щёлково Моск.обл.

Репутация: 5
Всего: 6



Цитата
не в проекте надо функцию обработки генерить на лету, в зависимости от состояния приложения

Ну, так давай подумаем вместе. Может чего родится интересное. Напиши поподробнее задачу.
PM MAIL ICQ   Вверх
Unregistered
Дата 1.10.2003, 06:16 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











Изначально задача была - каждому сообщению сопоставить функцию, адрес которой пишется в таблицу сообщений. Обработчик окна должен был искать сообщение в таблице и вызывать соответствующую функцию, причем хотелось, чтобы все функции принимали именно то, что им надо, т.е. OnCreate должна только принимать указатель на структуру LPCREATESTRUCT и больше ничего.
Но так, как придется использовать указатель на функцию, то для каждого такого типа функций нужно будет определять прототипы. Поэтому пока у меня все функции-обрабочики сообщений имеют один тип (принимают HWND,WPARAM и LPARAM), причем если они члены какого-либо класса, то все они статические, так как указатель для нестатических функций-членов получить нельзя. В этой реализации не все удобно, но вроде работает.
  Вверх
Fantasist
Дата 1.10.2003, 19:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лентяй
***


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

Репутация: 4
Всего: 41



Цитата
причем если они члены какого-либо класса, то все они статические, так как указатель для нестатических функций-членов получить нельзя


Почему нельзя, все можно. Естесственно, этот указатель не совсем указатель - это скорее смещение относительно экземпляра класса и не имеет смысла без экземпляра класса. Однако ведь тебе не нужно вызывать метод без экземпляра класса, не так ли?

Делаешь что-нибудь типа:

Код

class CMsgHandler
{
public:
virtual int HandleMsg(WPARAM wParam, LPARAM lParam) {return 0; };
virtual int HandleMsg2(WPARAM wParam, LPARAM lParam) {return 0; };
virtual int HandleMsg3(WPARAM wParam, LPARAM lParam) {return 0; };
};


typedef int (CMsgHandler::*handlerPointer)(int);

std::map<int,handlerPointer> message_map;

int main()
{
  message_map[WM_MSG1]=&CMsgHandler::HandleMsg;  
  message_map[WM_MSG2]=&CMsgHandler::HandleMsg2;  
  message_map[WM_MSG3]=&CMsgHandler::HandleMsg3;  
}

LRESULT CALLBACK CWnd::Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  СMsgHandler* msh=(СMsgHandler*)GetWindowLong(hwnd, 0);

  (msh->*message_map[uMsg])(wParam,lParam);
}



Это как идея. В реальности все схема будет слегка другая, но принцип тот же.

Это сообщение отредактировал(а) Fantasist - 1.10.2003, 19:26


--------------------
Волны гасят ветер...
PM MAIL   Вверх
mr.DUDA
Дата 1.10.2003, 22:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


3D-маньяк
****


Профиль
Группа: Экс. модератор
Сообщений: 8244
Регистрация: 27.7.2003
Где: город-герой Минск

Репутация: 25
Всего: 232



[/QUOTE]message_map[WM_MSG1]=&CMsgHandler::HandleMsg;[QUOTE]
Уверен, что можно получить указатель на нестатический компонент-функцию класса ? имхо, закавыка у Jcs именно со статическими членами класса - окна (не хочет он их использовать), а более никак не получить указатель на ф-цию / указатель на this внутри функции оконного класса. Последнее достижимо только с пом. GetWindowLong (как предложил RAN).


--------------------
user posted image
PM MAIL WWW   Вверх
Fantasist
Дата 2.10.2003, 01:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лентяй
***


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

Репутация: 4
Всего: 41



Цитата
Уверен, что можно получить указатель на нестатический компонент-функцию класса ?


Именно. Указатель на член класса. Таковой указатель, как я уже упоминал, не имеет смысла без указателя на экземпляр класса. Он сам по себе не является указателем, а является, так сказать "смещением" к члену класса. А вот экземпляр класса мы и храним в WindowLong.
Как я понял, проблема была в том, чтобы не делать switch в процедуре обработки окна, а делать динамический мэппинг сообщений к процедурам обработки. Вот примерно таким способом его можно и сделать.


--------------------
Волны гасят ветер...
PM MAIL   Вверх
RAN
Дата 2.10.2003, 10:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Экс. модератор
Сообщений: 709
Регистрация: 14.3.2003
Где: Щёлково Моск.обл.

Репутация: 5
Всего: 6



Jcs, давай по порядку:

Из твоего топика я понял, что ты хочешь объять необъятное. Тебе нужен универсальные механизм для создания карты сообщений. Причём функции могут быть как членами класса, так и обячными глобальными. Конечно, нет ничего невозможного, но это сложно. Моё предложение тогда такое: в CreateWindow последним параметром передавать либо указатель на экземпляр класса, либо NULL. А в обработчике сообщений анализировать это значение. Если оно NULL, то вызывать _cdecl, иначе _thiscall. Проблема заключается в том, как узнать адрес функции и в том, что если тебе нужно в одном и том же окне совмещать и thiscall и cdecl, то придётся при регистрации функции передавать уазатель на экземпляр класса или NULL. Фуф, ну и намудрил я. Например:
Код

typedef int (CMsgHandler::*handlerPointer)(int);

typedef struct {
   void* This;
   handlerPointer func;
} MessProc;

std::map<int,MessProc> message_map;


Как можно получить адрес функции - члена класса?
class A {
public:
void func();
};

A::func ?

Витруальную можно найти в vtable, но это тоже не хорошо. Потому что в каждом компиляторе этот механизм может реализовываться по своему. (то что в VC и Builder есть различия знаю точно. Сам проверял)

PM MAIL ICQ   Вверх
Jcs
Дата 2.10.2003, 14:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


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

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



Цена использования нестатических функций в карте сообщений выше, чем получаемые преимущества, поэтому обработчик конкретного мессаджа должен быть статическим. Карта сообщений сейчас являет собой динамически связанный список, хотя как мне кажется в том способе, который предложили fantasist и RAN поиск сообщений будет происходить бытсрее.
PM   Вверх
RAN
Дата 2.10.2003, 14:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Экс. модератор
Сообщений: 709
Регистрация: 14.3.2003
Где: Щёлково Моск.обл.

Репутация: 5
Всего: 6



Цитата
Карта сообщений сейчас являет собой динамически связанный список, хотя как мне кажется в том способе, который предложили fantasist и RAN поиск сообщений будет происходить бытсрее.

Это почему? map - это тоже список. Если тебя интересует скорость, то надо выделить память

typedef LRESULT (*MSG_PROC) (HWND , WPARAM ,LPARAM );
MSG_PROC* Map = new MSG_PROC[WM_USER];

Обнулить эту область памяти. И потом добавлять в массив функции обработки сообщений. Это будет самый быстрый map.
LRESULT CALLBACK CWnd::Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
MSG_PROC msgproc = Map[uMsg];
return( msgproc ? *msgproc(hwnd, wParam, lParam) : DefWindowProc( hwnd, uMsg, wParam, lParam ) );
}

PM MAIL ICQ   Вверх
Fantasist
Дата 2.10.2003, 18:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лентяй
***


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

Репутация: 4
Всего: 41



Цитата
Это почему? map - это тоже список.


map - это не список. Чаще всего это дерево, оптимизированное для быстрого поиска по ключу. Конечно, самый быстрый способ взятие значения по ключу целого типа - это хранить эти значения в массиве. Единственный недостаток - надо выделить память сразу под весь возможный диапазон ключей.
Как алтернатива - hash_map почти то же самое, что массив, но ограниченный.

Цитата
Как можно получить адрес функции - члена класса?


Я все выше уже написал. Внимательно посмотрите приведенный мною код. Это стардант С++.



--------------------
Волны гасят ветер...
PM MAIL   Вверх
RAN
Дата 2.10.2003, 19:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Экс. модератор
Сообщений: 709
Регистрация: 14.3.2003
Где: Щёлково Моск.обл.

Репутация: 5
Всего: 6



Цитата
Я все выше уже написал. Внимательно посмотрите приведенный мною код. Это стардант С++.

Хорошо. А можно динамически получать адрес виртуальной функции?

CMsgHandler obj;
obj.HandleMsg - адрес функции?

PM MAIL ICQ   Вверх
Fantasist
Дата 2.10.2003, 19:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лентяй
***


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

Репутация: 4
Всего: 41



Цитата
Хорошо. А можно динамически получать адрес виртуальной функции?


Точно так же. В моем примере функции как раз виртуальные. Адрес берется точно так же, просто указатель несет в себе метку о виртуальности, и при вычислении реального адреса функции (в момент использования) это учитывается.


--------------------
Волны гасят ветер...
PM MAIL   Вверх
RAN
Дата 2.10.2003, 19:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Экс. модератор
Сообщений: 709
Регистрация: 14.3.2003
Где: Щёлково Моск.обл.

Репутация: 5
Всего: 6



Нет, этот адрес получается на этапе компиляции. А я имею ввиду следующую ситуацию:

class base
{
public:
virtual void method() {.....}
};

class derived:public base
{
public:
virtual void method() {......}
};

.........................
base* obj = new derived;
..........................

Вопрос: какой адрес функции obj->method() ? Это будет тоже самое, что и &derived::method, хотя obj - указатель на объект класса base. На этапе компиляции этот адрес не известен. А в runtime его, по ходу, не получить.
PM MAIL ICQ   Вверх
Fantasist
Дата 2.10.2003, 20:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лентяй
***


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

Репутация: 4
Всего: 41



Цитата
Вопрос: какой адрес функции obj->method() ?


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


--------------------
Волны гасят ветер...
PM MAIL   Вверх
RAN
Дата 2.10.2003, 21:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Экс. модератор
Сообщений: 709
Регистрация: 14.3.2003
Где: Щёлково Моск.обл.

Репутация: 5
Всего: 6



Fantasist, я это всё прекрасно понимаю. Не первый год с ООП.
Страуструпа у меня нет. Искать не хочется, так как проблемы такой передо мной не стоит. Но было бы очень интесно. Может кто знает ответ. Вот и написал.
PM MAIL ICQ   Вверх
Fantasist
Дата 3.10.2003, 05:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лентяй
***


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

Репутация: 4
Всего: 41



Цитата
Fantasist, я это всё прекрасно понимаю. Не первый год с ООП.


А в чем тогда вопрос? В чем сомнения? И к чему тогда:

Цитата
Вопрос: какой адрес функции obj->method() ? Это будет тоже самое, что и &derived::method, хотя obj - указатель на объект класса base. На этапе компиляции этот адрес не известен. А в runtime его, по ходу, не получить.


?


--------------------
Волны гасят ветер...
PM MAIL   Вверх
Jcs
Дата 3.10.2003, 08:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


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

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



Все работает, как и описал Fantasist, причем и для дочерних классов, а смещене дло функции от начала экземпляра одинаковое и у родителя и у потомка. Есть ли у кого-нить электронный Страустрап?
Буду очень признателен, если кто-нибудь вышлет. [email protected]
PM   Вверх
RAN
Дата 3.10.2003, 09:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Экс. модератор
Сообщений: 709
Регистрация: 14.3.2003
Где: Щёлково Моск.обл.

Репутация: 5
Всего: 6



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

class base
{
public:
virtual void method() {.....}
};

class derived:public base
{
public:
virtual void method() {......}
};

.........................
base* obj = new derived;
..........................

void* GetMethodAddr(base* x)
{
......................Как тут получить адрес x->Method() ? ...............................
}

PM MAIL ICQ   Вверх
Jcs
Дата 3.10.2003, 13:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


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

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



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

typedef int (CWnd::*fptr)(WPARAM,LPARAM);
class CWnd
{
public:
virtual int onCreate(WPARAM wParam,LPARAM lParam); {...}
private:
CMsgMap* pMsgMap;  // ката сообщений
};
class CMainWnd: public CWnd
{

};
int CMainWnd::onCreate()
{...}

int main()
{
fptr onMsg;
CMainWnd* pMainWnd = new CMainWnd;

       onMsg = (fptr)(&CMainWnd::onCreate);
pMsgMap->AddMsh(WM_CREATE,onMsg);  // запихиваем сообщение в карту
}
struct MappedMsg
{
 UINT MessageID;
 fptr onMsg;
}

CMsgMap::ProcessMsg(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam) // вызывается из обработчика окна
{
 CWnd* pWnd = (CWnd*)GetWindowLong(hWnd,GWL_USERDATA);
 MappedMsg* mm = SearchMsg(msg);   // получаем указатель на структуру
 (pWnd->*mm->onMsg)(wParam,lParam);   // вызываем функцию
}


PM   Вверх
RAN
Дата 3.10.2003, 15:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Экс. модератор
Сообщений: 709
Регистрация: 14.3.2003
Где: Щёлково Моск.обл.

Репутация: 5
Всего: 6



Цитата
При наследовании новые члены дочернего класса, если таковые имеются, добавляются в конец класса, поэтому все наследуемые функции, в том числе и виртуальные, имеют одно и то же смещение от начала

Не виртуальные функции никуда не добавляются. Они связываются на этапе компиляции. Про смещение можно говорить только, когда имеешь дело с виртуальными функциями. У тебя,Jcs, не совсем верное представление о функционировании наследования. В объекте класса храниться указатель на vtable - виртуальную таблицу. Эта виртуальная таблица одна на все объекты данного класса. А не виртуальные функции туда вовсе не помещаются.

И вообще, если это в ответ на мой вопрос, то меня похоже никто не понимает. Жаль. Ну, да ладно. Это так. От любознательности и для общего развития smile.gif .
PM MAIL ICQ   Вверх
Fantasist
Дата 3.10.2003, 20:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лентяй
***


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

Репутация: 4
Всего: 41



Цитата
И вообще, если это в ответ на мой вопрос, то меня похоже никто не понимает


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


--------------------
Волны гасят ветер...
PM MAIL   Вверх
RAN
Дата 3.10.2003, 22:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Экс. модератор
Сообщений: 709
Регистрация: 14.3.2003
Где: Щёлково Моск.обл.

Репутация: 5
Всего: 6



Цитата
Если ты хочешь по указателю на экземпляр объекта понять, какой из этих адресов будет использоваться, то надо залезть в vtable. Но для того, чтобы вызывать по указателю на член виртуальную функцию вовсе не нужно получать адрес этой функции. Потому как средствами языка это невозможно


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

Короче, спасибо за интересную дискуссию.
PM MAIL ICQ   Вверх
DENNN
Дата 4.10.2003, 16:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник Клуба
Сообщений: 3878
Регистрация: 27.3.2002
Где: Москва

Репутация: 1
Всего: 43



Цитата
Короче, спасибо за интересную дискуссию.

Я присоединяюсь и предлагаю добавить эту тему в FAQ- вещь вобщем то нужная.
PM ICQ   Вверх
mr.DUDA
Дата 5.10.2003, 11:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


3D-маньяк
****


Профиль
Группа: Экс. модератор
Сообщений: 8244
Регистрация: 27.7.2003
Где: город-герой Минск

Репутация: 25
Всего: 232



ЗЫ, а ещё ведь есть виртуальные базовые классы wink.gif

Вот вам задачка. Если класс порожден вот так:
Код
class A {virtual void M();};
class B: virtual public A {virtual void M();};
class C: virtual public A {virtual void M();};

class D: public B, C {virtual void M();};

то как определить, что лежит в vtable класса D ?


--------------------
user posted image
PM MAIL WWW   Вверх
Fantasist
Дата 6.10.2003, 06:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Лентяй
***


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

Репутация: 4
Всего: 41



Цитата(mr @ 5.10.2003, 08:29)
то как определить, что лежит в vtable класса D ?

Что значит как определить? huh2.gif
Вестимо, там лежит D::M. Или ты ожидаешь что конструкция:

A* a=new D();
a->M();

вызовет A::M()? smile.gif


--------------------
Волны гасят ветер...
PM MAIL   Вверх
mr.DUDA
Дата 6.10.2003, 14:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


3D-маньяк
****


Профиль
Группа: Экс. модератор
Сообщений: 8244
Регистрация: 27.7.2003
Где: город-герой Минск

Репутация: 25
Всего: 232



Это я про внутренние потроха vtable, в ответ на:
Цитата
Все работает, как и описал Fantasist, причем и для дочерних классов, а смещене дло функции от начала экземпляра одинаковое и у родителя и у потомка.




--------------------
user posted image
PM MAIL WWW   Вверх
RAN
Дата 9.10.2003, 08:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Экс. модератор
Сообщений: 709
Регистрация: 14.3.2003
Где: Щёлково Моск.обл.

Репутация: 5
Всего: 6



Хочу ещё поблагодарить за интересную тему. Она мне подсказала интересное решение в моём приложении (по крайней мере, мне нравиться). Суть в следующем:

Есть оконный класс (свой собственный, естественно). Сделан он так, как я описывал выше. Т.е. в классе есть статическая функция, выполняющая роль оконной процедуры. Эта функция получает указатель на объект класса по дескриптору окна (GetWindowLong). Потом вызывается соответствующая сообщению функция.
Так вот в окне можно кое-что "перетаскивать" мышкой. Возникла идея написать две разные функции для обработки события WM_MOUSEMOVE. Скажем, OnMouseMove1() и OnMouseMove2(). Ввожу переменную для хранения указателя на текущюю функцию обработки. Меняю значение этой переменной в зависимости от обстоятельств.

Код

class CWnd {
private:
.......................................................
static LRESULT CALLBACK Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
.......................................................
void (CWnd::*OnMouseMove)(int ,int );
void OnMouseMove1(int xPos, int yPos);
void OnMouseMove2(int xPos, int yPos);
.......................................................
};

LRESULT CALLBACK CWnd::Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch( uMsg )
{
.............................................................................................
case WM_MOUSEMOVE:
 {
  CWnd* p = (CWnd*)GetWindowLong(hwnd, 0);
  void (CWnd::*MouseProc)(int ,int );
  MouseProc = p->OnMouseMove;
  (p->*MouseProc)(LOWORD(lParam), HIWORD(lParam));
 }
 break;
.................................................................................................
}
return( DefWindowProc( hwnd, uMsg, wParam, lParam ) );
}


Всё работает. Но если не вводить дополнительную переменную MouseProc, то компилятор VC ругается

(p->*OnMouseMove)(LOWORD(lParam), HIWORD(lParam));
error C2597: illegal reference to data member 'CWnd::OnMouseMove' in a static member function
error C2568: '->*' : unable to resolve function overload

В другом месте в не статической функции другая проблема:
(*OnMouseMove)(xPos, yPos);
error C2171: '*' : illegal on operands of type 'void (__thiscall CWnd::*)(int,int)'
error C2064: term does not evaluate to a function

Написал (this->*OnMouseMove)(xPos, yPos); и всё OK.



Может кто объяснит логику происходящего


PM MAIL ICQ   Вверх
Jcs
Дата 9.10.2003, 15:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


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

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



В первом случае ты пытаешься из статической фунуции обратиться к нестатическому члену класса, а во втором случае я не уверен, но, как мне кажется, функция должна знать адрес конкретного экземпляра класса, для корректного вызова.
PM   Вверх
RAN
Дата 9.10.2003, 15:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Экс. модератор
Сообщений: 709
Регистрация: 14.3.2003
Где: Щёлково Моск.обл.

Репутация: 5
Всего: 6



Цитата
ты пытаешься из статической фунуции обратиться к нестатическому члену класса

Ну да. И в чём проблема. Я постоянно обращаюсь и нет проблем. А вызвать функцию по указателю - нелегальная ссылка, видите ли.
PM MAIL ICQ   Вверх
RAN
Дата 9.10.2003, 16:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Экс. модератор
Сообщений: 709
Регистрация: 14.3.2003
Где: Щёлково Моск.обл.

Репутация: 5
Всего: 6



Но я и сам понял.
Цитата

функция должна знать адрес конкретного экземпляра класса, для корректного вызова

Так и есть

Начну со второго примера.
Пишу (*OnMouseMove)(xPos, yPos);. VC думает, что я пытаюсь разыменовать переменную типа 'void (__thiscall CWnd::*)(int,int)'. "Нет, - говорит, - нельзя. Без this не можно так делать."
Исправляю на (this->*OnMouseMove). "Ага, - говорит VC, - это другое дело. Вызвать можно. this - указатель на CWnd, две переменные типа int в качестве параметров. Всё OK."

А в первом случае из-за этого же другая проблема.
Надо было писать так:
(p->*(p->OnMouseMove))(LOWORD(lParam), HIWORD(lParam));
Так и написал. Всё понял.

P.S. Это однозначно надо в FAQ.
ВЕЛИК И МОГУЧ ЯЗЫК C++


PM MAIL ICQ   Вверх
Страницы: (3) [Все] 1 2 3 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

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

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

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

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


 




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


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

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