Ваш код вызывает неопределенное поведение , пытаясь вызвать член класса B, используя объект класса A. Мы можем попытаться объяснить, как компилятор может прийти к поведению, которое вы наблюдали, но тамНе гарантируется, что вы получите такое же поведение, если вы что-то измените (добавите / удалите элемент, измените настройки компилятора или используйте другой компилятор).
С приведением в макросе HANDLER
вы говоритекомпилятор не предупреждает вас об использовании несовместимых типов, а просто делает так, как вы говорите.В этом случае вы указываете компилятору переинтерпретировать адрес члена любого класса как адрес члена класса A.
Когда вы позже попытаетесь вызвать, например, B::multiply
,Функция не знает, что она не работает с объектом класса B, поэтому она с радостью ударит байты aA
, которые будут соответствовать элементу B::count
, если бы он был объектом B
.Скорее всего, эти байты на самом деле используются A::funcs
, но, видимо, не для чего-либо критического.Если вы измените класс A на:
class A
{
int count;
std::map< int, MEMFUNC > funcs;
public:
A() : count(0) { AddLocals(); }
~A() {}
int CallLocal(int nID, int x, int y)
{
MEMFUNC f = funcs[nID];
if (f) return (this->*f)(x, y);
else return 0;
}
int Count()
{
return count;
}
void AddLocals()
{
Add(ADD, HANDLER(A, plus));
Add(MUL, HANDLER(B, multiply));
Add(SUB, HANDLER(A, subtract));
Add(DIV, HANDLER(B, divide));
}
void Add(int nID, MEMFUNC f) { funcs[nID] = f; }
int plus(int x, int y) { return x+y; }
int subtract(int x, int y) { return x-y; }
};
, то печать результата aA.Count()
в разных местах может показать эффект.
Компилятор вызывает ожидаемую функцию, потому что они не являютсяВиртуальный член функции.Единственное различие между не-функциями-членами и не-виртуальными функциями-членами заключается в скрытом аргументе, который передает указатель this
в функции-члене.Таким образом, если вы возьмете адрес не-виртуальной функции-члена, вы получите фиксированный адрес, который отличается для каждой функции.Если бы функции-члены были виртуальными, то компилятор, скорее всего, вернул бы индекс в v-таблицу в качестве указателя на эту функцию (вместе с каким-то указанием на то, что это смещение v-таблицы).Затем код может определить на месте вызова, может ли он выполнить прямой вызов функции-члена или нужно ли выполнить косвенный вызов через v-таблицу объекта, для которого вызывается функция.