1

Вдохновленный этим cppcon talk by Richard Powell я создал следующий фрагмент кода пошалить:Почему классы с виртуальными функциями выровнены иначе, чем классы без?

#include <iostream> 
using std::cout; 
using std::endl; 

struct erdos 
{ 
    void who() 
    { 
    cout << "erdos" << endl; 
    } 
    float f1; 
    float f2; 
}; 

struct fermat : public erdos 
{ 
    float f3; 
}; 

struct fermat2 : public fermat 
{ 
    float f4; 
}; 

struct fermat3 : public fermat2 
{ 
    float f5; 
}; 

int main(void) 
{ 
    erdos e; 
    cout << "sizeof(e)" << sizeof(e) << endl; 
    fermat f; 
    cout << "sizeof(f)" << sizeof(f) << endl; 
    fermat2 f2; 
    cout << "sizeof(f2)" << sizeof(f2) << endl; 
    fermat3 f3; 
    cout << "sizeof(f3)" << sizeof(f3) << endl; 
    cout << "sizeof(void*)" << sizeof(void*) << endl; 
    cout << "sizeof(float)" << sizeof(float) << endl; 
    return 0; 
} 

, который будет печатать:

sizeof(e)8 
sizeof(f)12 
sizeof(f2)16 
sizeof(f3)20 
sizeof(void*)8 
sizeof(float)4 

После добавления virtual к who() я получаю эту

sizeof(e)16 
sizeof(f)24 
sizeof(f2)24 
sizeof(f3)32 
sizeof(void*)8 
sizeof(float)4 

Теперь, добавив размер void* в структуру, просто, но почему будет ли это дополнение (о чем также упоминается Ричардом в его разговоре) в виртуальном случае, а не в не виртуальном случае?

sizeof(e)16 - 8 = 8 
sizeof(f)24 - 8 = 16 but is in fact 12 (padding 4) 
sizeof(f2)24 - 8 = 16 matches 
sizeof(f3)32 - 8 = 24 but is in fact 20 (padding 4) 

Я проверил его с GCC 5.3.0 и лязгом 3.7.1 на Ubuntu 14.04 64 битного

+5

Как вы _know_ это дополнение? Возможно, это дополнительные данные, такие как смещения и указатели на таблицы. Это деталь реализации и не гарантируется, что она будет одинаковой для всех компиляторов. –

+2

Вы добавили таблицу виртуальных методов, которая раньше не существовала. – Donnie

ответ

5
sizeof(void*)8 

Ну, есть свой ответ.

Предполагая, что вашей реализации нужен только указатель для обработки виртуального поиска, это является причиной выравнивания. Указателю 64-битной компиляции требуется 64-битное пространство (поэтому мы называем его «64-разрядным»). Но он также нуждается в 64-битном выравнивании.

Следовательно, любая структура данных, в которой хранится указатель в 64-битной компиляции, также должна быть согласована с 64-битным. Выравнивание объекта должно быть выровнено по 8 байт, а размер должен быть заполнен до 8 байтов (для причин индексации массива). Вы увидите то же самое, если бы указатель одного из float.

+0

Так что вы говорите, что указатели (на функции) должны быть выровнены, а для членов (например, в не виртуальном случае, который я представил) нет? Вот почему не виртуальный f3 имеет размер 'sizeof (f3) 20'? – Patryk

+2

@Patryk: не виртуальный 'f3' содержит 5 элементов размера 4. 5 × 4 = 20.' float', вероятно, соответствует 4 байтам. Поскольку 20 делится на 4, никакие дополнения не нужны. Виртуальный 'f3 содержит 5 членов размером 4 и еще один с размером 8. 5 × 4 + 8 = 28.' float', вероятно, имеет 4 байта, указатель, вероятно, выровнен на 8 байт. Поскольку используется требование более высокого требования, структура имеет 8-байтовую арифметику. Поскольку 20 не делится на 8 равномерно, компилятор должен добавить дополнение к ближайшему краю 8, что равно 32. Конечно, это просто спекуляция. Компилятор может просто решить добавить 2 6-байтовых поля без требований к элегированию. –

+0

@Revolver_Ocelot Большое спасибо. Это проливает больше света на эту тему – Patryk