Размер классов с виртуальными функциями GCC / Xcode - PullRequest
1 голос
/ 29 июля 2011

Может кто-нибудь объяснить мне, что здесь происходит? Во-первых, я думаю, что большинство программистов знают, что класс с виртуальной функцией имеет vtbl и, следовательно, имеет 4 дополнительных байта в верхней части. Насколько я знаю, это довольно стандартно. Я проверил это и воспользовался этим, прежде чем выполнять загрузку из двоичного файла с исправленными vtbls. В течение последних 6 месяцев я работал в Xcode и совсем недавно столкнулся с необходимостью сделать что-то загрузочное на месте, поэтому я снова начал искать исправления vtbls. Просто чтобы убедиться, что мое понимание верно, я написал пример программы. Вот оно:

class A
{
    public:
    virtual int getData()
    {
        return a;
    }

    virtual void print()
    {
        printf("hello\n");
    }
    int a;
};

class B : public A
{
public:
    int getData()
    {
        return b;
    }
    int b;
};

class C : public B
{
public:
    int getData()
    {
        return c;
    }

    void print()
    {
        printf("world\n");
    }
    int c;
 };

class D
{
public:
    int a;
    int b;
};

int main (int argc, const char * argv[])
{
    A* tA = new A();
    tA->a = 1;

    printf("A: %d\n", sizeof(A));
    printf("A data: %d\n", tA->getData());

    B* tB = new B();
    tB->a = 2;
    tB->b = 4;

    printf("B: %d\n", sizeof(B));
    printf("B data: %d\n", tB->getData());

    C* tC = new C();
    tC->c = 8;

    printf("C: %d\n", sizeof(C));
    printf("C data: %d\n", tC->getData());

    A* aC = tC;
    aC->print();

    printf("D: %d\n", sizeof(D));

    return 0;
}

Мой ожидаемый результат был:

A: 8

Данные: 1

B: 12 ​​

B данные: 4

C: 16

C данные: 8

мир

D: 8

Однако вывод, который я получаю:

A: 16

Данные: 1

B: 16

B данные: 4

C: 24

C данные: 8

мир

D: 8

Кто-нибудь знает, что здесь происходит? Спасибо!

1 Ответ

3 голосов
/ 29 июля 2011

Экземпляры классов от A до C содержат vptr, указатель на таблицу виртуальных функций для динамического типа. Этот указатель занимает 8 байтов на вашей 64-битной машине (или 4 байта на 32-битной машине). Каждый элемент int занимает 4 байта.

Минимальное значение sizeof (Class) - это сумма sizeof (member) для всех членов. Если бы это было так, то

sizeof(A) = 8 (vptr) + 4 (int a) = 12
sizeof(B) = 8 (vptr) + 4 (int a) + 4 (int b) = 16
sizeof(C) = 8 (vptr) + 4 (int a) + 4 (int b) + 4 (int c) = 20
sizeof(D) = 4 (int a) + 4 (int b) = 8

Однако, это только размер минимум . Компиляторы обычно увеличивают этот размер до кратного размера (void *), который здесь составляет 8 байт. Этот процесс называется выравниванием . Может показаться, что это тратит впустую память, но это перевешивает выигрыш в производительности: ЦП может читать выровненные данные намного быстрее, чем несогласованные.

Кстати, ваш ожидаемый результат был бы правильным, если бы вы работали на 32-битной машине. Указатели (особенно vptr) имеют ширину 4 байта, и выравнивание также должно быть кратно 4 байтам. Поскольку все члены данных рассматриваемых классов имеют размер 4 байта, выравнивание здесь ничего не даст.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...