Заранее оговорюсь о том, что конкретные значения размеров указателей могут варьироваться от компилятора к компилятору. Значения приведенные в данной статье верны для MS Visual Studio 2005, 2008. Однако основной интерес в том, что они не равны 4-м байтам.
Сначала приведем основной код, который и будет рассматриваться в данной статье
class Base
{
public:
void f()
{
std::cout << "Base";
}
};
Использовать методы этих классов можно следующим образом:
создаем указатель на метод класса Base
void(Base::* F) ();
присваиваем ему адрес метода класса Base
F = &Base::f;
Для нас самым важным в этом отрывке было то, что размер указателя F после присвоения равен 4-м байтам. В общем, необязательно даже что-то присваивать этому указателю. Если убрать последнюю строчку и просто вызвать sizeof(F), то результат тоже будет 4.
При добавлении еще одного класса
class Base1
{
public:
int m_base1;
void f()
{
std::cout << "Base1";
}
};
и наследовании от него ABase
class ABase : public Base, public Base1 ...
указатель F объявленный вот так
void(ABase::* F) ();
станет равен 8-ми байтам. Структура указателя будет такой:
--------------
|adress|index|
--------------
4 + 4 = 8 байт
где adress - адрес функции, index - смещение, необходимое для вычисления this для вызова соответствующей функции.
И наконец, доведем нашу ситуацию до абсурда. Если ABase унаследовать виртуально от обоих родителей
class ABase : virtual public Base, virtual public Base1 ...
то размер указателя F станет равным 12-ти байтам. Его структура станет такой
----------------------
|adress|index|vbIndex|
----------------------
4 + 4 + 4 = 12 байт
где adress - адрес функции, index - смещение, необходимое для вычисления this для вызова соответствующей функции, vbIndex - смещение, необходимое, для узнавания местарасположения таблицы виртуальных базовых классов.