Pages

Friday, July 24, 2009

C++ классы: размещение в памяти. Часть IV.

"Пустой" класс с виртуальным методом имеет размер 4 байта. Можно добавить еще один или несколько виртуальных методов, но размер не меняется:

#include <stdio.h>
class CEmpty
{
public:
virtual ~CEmpty() {};
};
class CEmpty2
{
public:
virtual ~CEmpty2() {};
virtual void nothing() {};
};
class CEmpty3
{
public:
virtual ~CEmpty3() {};
virtual void nothing() {};
virtual void something() {};
};
int main()
{
CEmpty empty;
printf("size of CEmpty object is %d\r\n", sizeof(empty));
CEmpty2 empty2;
printf("size of CEmpty2 object is %d\r\n", sizeof(empty2));
CEmpty3 empty3;
printf("size of CEmpty3 object is %d\r\n", sizeof(empty3));
return 0;
}



Програмки из предыдущей части, если классы имеют виртуальные функции, не работают.
Если посмотреть память, то видно, что переменные передвинулись на 4 байта в памяти от "начала" класса:

Это хорошо видно, если использовать класс со строкой из предадущих примеров:

#include <stdio.h>
#include <Windows.h>
class CCharArray
{
char x[24];
public:
CCharArray()
{
strcpy(x, "Hello");
}
virtual void out()
{
printf("x is %s\r\n", x);
}
};
int main()
{
CCharArray objCharArray;
printf("size of CCharArray object is %d\r\n", sizeof(objCharArray));
printf("CCharArray as string is %s\r\n", (char*)&objCharArray);
objCharArray.out();
return 0;
}



Строка
printf("CCharArray as string is %s\r\n", (char*)&objCharArray);
не работает.

Добавили только виртуальный метод. Память изменилась. Вывод - это указатель на виртуальные функции. Точнее - на таблицу виртуальных функций. Эту таблицу можна просмотреть:

#include <stdio.h>

class C1Int
{
int x;

public:
virtual void test()
{
printf("C1Int::test()\r\n");
}
};

class C2Int : public C1Int
{
int y;

public:
virtual void test()
{
printf("C2Int::test()\r\n");
}

virtual void test1()
{
printf("C2Int::test1()\r\n");
}

virtual void test2()
{
printf("C2Int::test2()\r\n");
}
};

int main()
{
C2Int two_int;
printf("size of C2Int object is %d\r\n", sizeof(two_int));
printf("Address of C2Int object is %p\r\n", &two_int);

int* p = (int*)(&two_int);
printf("Address of C2Int pointer is %p\r\n", p);

int p1 = *((int*)(&two_int));
printf("Value of this pointer is %x\r\n", p1);

int* _p1 = (int*)*((int*)(&two_int));
printf("Same.. pointer is %p\r\n", _p1);

int* p2_1 = (int*)*((int*)*(int*)(&two_int));
printf("Value of first entry of C2Int VTable is %p\r\n", p2_1);

int* p2_2 = (int*)*((int*)*(int*)(&two_int) + 1);
printf("Value of second entry of C2Int VTable is %p\r\n", p2_2);

int* p2_3 = (int*)*((int*)*(int*)(&two_int) + 2);
printf("Value of third entry of C2Int VTable is %p\r\n", p2_3);

int* p2_4 = (int*)*((int*)*(int*)(&two_int) + 3);
printf("Value of fourth entry of C2Int VTable is %p\r\n", p2_4);

int* p2_5 = (int*)*((int*)*(int*)(&two_int) + 4);
printf("Value of fifth entry of C2Int VTable is %p\r\n", p2_5);

return 0;
}



C2Int class имеет только 3 виртуальных метода, но программа пытается найти 5. Эта "ошибка" показывает, что таблица заканчивается нулем.
Функции нашлись. Их и вызвать можна не спрашивая разрешения у класса:

#include <stdio.h>

class C1Int
{
public:
virtual void test()
{
printf("C1Int::test()\r\n");
}
};

class C2Int : public C1Int
{
public:
virtual void test()
{
printf("C2Int::test()\r\n");
}

virtual void test1()
{
printf("C2Int::test1()\r\n");
}

virtual void test2()
{
printf("C2Int::test2()\r\n");
}
};

typedef void (* func)();

int main()
{
C2Int two_int;
printf("size of C2Int object is %d\r\n", sizeof(two_int));
printf("Address of C2Int object is %p\r\n", &two_int);

int* p = (int*)(&two_int);
printf("Address of C2Int pointer is %p\r\n", p);

int p1 = *((int*)(&two_int));
printf("Value of this pointer is %x\r\n", p1);

int* _p1 = (int*)*((int*)(&two_int));
printf("Same.. pointer is %p\r\n", _p1);

int* p2_1 = (int*)*((int*)*(int*)(&two_int));
printf("Value of first entry of C2Int VTable is %p\r\n", p2_1);

int* p2_2 = (int*)*((int*)*(int*)(&two_int) + 1);
printf("Value of second entry of C2Int VTable is %p\r\n", p2_2);

int* p2_3 = (int*)*((int*)*(int*)(&two_int) + 2);
printf("Value of third entry of C2Int VTable is %p\r\n", p2_3);

func f1 = (func)p2_1;
f1();

f1 = (func)p2_2;
f1();

f1 = (func)p2_3;
f1();

C1Int one_int;
func f2 = (func)((int*)*((int*)*(int*)(&one_int)));
f2();

return 0;
}


No comments:

Post a Comment