Причуда на оформлението на MSVC обект

Имам прост клас в C++, който има цяло число и vtable:

class Something {

   virtual void sampleVirtualMethod();

   int someInteger;
};

Ако погледнете оформлението на обекта за MSVC (използвайки /d1reportSingleClassLayout), получавате:

class Something       size(8):
        +---
 0      | {vfptr}
 4      | someInteger
        +---

Което има пълен смисъл. 4 байта за указателя на vtable и 4 байта за цялото число. Странното е, когато добавя двойно към класа:

class Something {    
    virtual void sampleVirtualMethod();
    int someInteger;
    **double someDouble;**
};

Получавам това оформление на обекта:

class Something       size(24):
        +---
 0      | {vfptr}
 8      | someInteger
        | <alignment member> (size=4)
16      | someDouble
        +---

Защо разликата между отместването 0 и someInteger е 8 вместо 4? Нарасна ли vtable до 8 байта по някакъв начин? Независимо от реда на добавяне на двойно, това се случва.

Благодаря.


person Mason    schedule 12.02.2010    source източник
comment
Не мога да не се чудя защо ви е грижа за това. Това причинява ли пряк проблем на това, върху което работите?   -  person Benoît    schedule 12.02.2010
comment
Защо не се интересува от това? Това е изненадващо за ОП (и за мен). Каква е вредата в опитите да научите нови неща? ;)   -  person jalf    schedule 12.02.2010
comment
Опитвам се да обединя обектни модели между двоични файлове и двоични файлове на визуално студио. Всъщност е проблем :). Странното е, че и GCC, и Visual Studio излагат обекти като този, докато llvm не го прави.   -  person Mason    schedule 12.02.2010
comment
В програма от реалния живот бих се интересувал от това много, ако обектът е инстанциран много пъти. Въздействието на загубата на памет би било опустошително.   -  person AnT    schedule 13.02.2010
comment
Проблемът тук е, че размерът на vfptr не може да се промени на 8. Той все още е 4. Но тъй като отместването на член int се промени на 8, между тях трябва да има <alignment member> с размер 4. В доклада обаче няма нищо. Това просто няма смисъл. Прилича на бъг.   -  person AnT    schedule 13.02.2010
comment
Вижте stackoverflow.com/questions/892767/   -  person Macke    schedule 13.02.2010


Отговори (3)


Тази публикация в блог обсъжда същия проблем и включва обяснение в коментари от Jan Gray, който написа кода за оформление на MS C++ компилатора преди много време.

За да перифразираме, vfptr се вмъква в оформлението на класа само след като другите членове на данните са били изложени. В зависимост от изискванията за подравняване на членовете на данните, това означава, че може да се въведе ненужно подпълване. Освен това това се случва само ако класът няма базов клас с vfptr.

Следователно заобиколното решение, което е представено в публикацията в блога:

class EmptyBase
{
protected:
    virtual ~EmptyBase() {}
};

class Something : public EmptyBase {    
    virtual void sampleVirtualMethod();
    int someInteger;
    **double someDouble;**
};

sizeof(Something) трябва да бъде 16 в този случай.

person honggoff    schedule 25.02.2014

Подозирам, че този отговор има нещо общо с то. За да цитирам от отговора на dirkgently, като цитирам ръководството на GCC:

Обърнете внимание, че подравняването на всеки даден тип структура или обединение се изисква от стандарта ISO C да бъде поне пълно кратно на най-ниското общо кратно на подравняванията на всички членове на въпросната структура или обединение.

Съгласно това правило, след като добавите 8-байтово двойно, компилаторът трябва да подреди всичко в 8-байтови кратни. Можете да отмените това с #pragma pack(), разбира се, въпреки че ще бъде по-малко ефективно, ако завършите с 8-байтов член на нещо различно от 8-байтова граница.

person aalpern    schedule 13.02.2010
comment
Не, не става. Единственото нещо, което се казва в цитата, е, че поради double член цялата структура трябва да бъде подравнена на 8-байтова граница. Но това не обяснява защо подравняването на целочислен член се промени. Тази структура трябва да се побере в 16 байта, като всички изисквания за подравняване са изпълнени перфектно. Но компилаторът изведе 24. Защо? - person AnT; 13.02.2010
comment
Интересното е, че тази страница с примери за подравняване за x64 код във VC++ 8 показва същото нещо със структура, съдържаща int и double, но за съжаление не казва защо. msdn.microsoft.com/en-us/library/71kf49f1 (VS.80).aspx Ясно е, че компилаторът избира да подреди членовете по най-малкия общ знаменател, който е 8, следователно необходимостта от подравняване. Вероятно има място за тълкуване в това изречение. - person aalpern; 13.02.2010
comment
aaplem - тази връзка показва същото нещо с int и double. Това вероятно е, защото искате удвояванията да бъдат подравнени по 8 байта. Винаги съм смятал, че наличието на 4-байтови подравнени двойки е супер бавно на x86. - person Mason; 14.02.2010
comment
@Mason - съгласен; Определено искам двойните ми да са подравнени с 8 байта! Просто се чудя дали подравняването на членовете на данните се обработва независимо от показалеца на vtbl. Документите на MS говорят за подравняването на членовете на данните, но не намерих никъде, където те показват дали vtbl се счита за един от тези членове или не. И минаха 10 години, откакто работих по проект, който ме накара да мисля за vtbl оформлението, така че забравих повечето от това, което някога знаех :-) (и това беше за VC++ 4.2, така или иначе, сега може да е различно) . - person aalpern; 15.02.2010

Не мога да отговоря директно на въпроса ви, защото няма добро извинение за поведението на компилатора. Вместо това ще се отдам на диви спекулации, тъй като все още няма отговор.

Подозирам грешка в алгоритъма за подравняване, която е нещо подобно:

  • подравняването на структурата (т.е. разположението на първия елемент) трябва да бъде поне толкова широко, колкото подравняването на най-широкия елемент
  • опа, забравих да преброя указателя на таблицата с виртуални функции като член

Ако тази грешка съществува, подозирам, че е останала от ранните дни на компилатора като 4-байтов C компилатор. Сега по подразбиране за компилатора е /Zp8, което означава, че всяка структура е подравнена поне до 8 байта, така че няма да има нужда да се коригира подравняването на "първия" член в този случай така или иначе.

Поздрави, Шерм

person Sherm    schedule 12.02.2010