gas — C ++ Понимание того, как виртуальный деструктор добавляет размер к типу с точки зрения выходов сборки

Я пытаюсь понять более глубоко, почему выходные данные следующей программы таковы (см. Ниже оба). В то же время я пытаюсь понять, как это относится к сборочной версии программы. Главным образом, где находятся типы в ассемблерном коде и что делает один размер больше другого? Где в виртуальной таблице, что делает дочерний класс больше, чем тестовый класс?

КОД

#include <iostream>

class parent {
int glove;
public:
parent() {}
virtual ~parent() {}
};

class child : public parent {
int ball;
public:
child() {}
~child() {}
};

class test {
int test1;
int test2;

public:
test() {}
};

int main() {
std::cout << "Size of child is " << sizeof(child) << std::endl;
std::cout << "Size of test is " << sizeof(test) << std::endl;
}

ВЫХОД

Size of child is 12
Size of test is 8

СБОРКА

.file   "test.cpp".local  _ZStL8__ioinit
.comm   _ZStL8__ioinit,1,1
.section    .rodata
.LC0:
.string "Size of child is ".LC1:
.string "Size of test is ".text
.globl main
.type   main, @function
main:
.LFB974:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl   %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl    %esp, %ebp
.cfi_def_cfa_register 5
andl    $-16, %esp
subl    $16, %esp
movl    $.LC0, 4(%esp)
movl    $_ZSt4cout, (%esp)
call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl    $12, 4(%esp)
movl    %eax, (%esp)
call    _ZNSolsEj
movl    $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)
movl    %eax, (%esp)
call    _ZNSolsEPFRSoS_E
movl    $.LC1, 4(%esp)
movl    $_ZSt4cout, (%esp)
call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl    $8, 4(%esp)
movl    %eax, (%esp)
call    _ZNSolsEj
movl    $_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_, 4(%esp)
movl    %eax, (%esp)
call    _ZNSolsEPFRSoS_E
movl    $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE974:
.size   main, .-main
.type   _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB984:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl   %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl    %esp, %ebp
.cfi_def_cfa_register 5
subl    $24, %esp
cmpl    $1, 8(%ebp)
jne .L5
cmpl    $65535, 12(%ebp)
jne .L5
movl    $_ZStL8__ioinit, (%esp)
call    _ZNSt8ios_base4InitC1Ev
movl    $_ZNSt8ios_base4InitD1Ev, %eax
movl    $__dso_handle, 8(%esp)
movl    $_ZStL8__ioinit, 4(%esp)
movl    %eax, (%esp)
call    __cxa_atexit
.L5:
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE984:
.size   _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
.type   _GLOBAL__I_main, @function
_GLOBAL__I_main:
.LFB985:
.cfi_startproc
.cfi_personality 0x0,__gxx_personality_v0
pushl   %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl    %esp, %ebp
.cfi_def_cfa_register 5
subl    $24, %esp
movl    $65535, 4(%esp)
movl    $1, (%esp)
call    _Z41__static_initialization_and_destruction_0ii
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE985:
.size   _GLOBAL__I_main, .-_GLOBAL__I_main
.section    .ctors,"aw",@progbits
.align 4
.long   _GLOBAL__I_main
.weakref    _ZL20__gthrw_pthread_oncePiPFvvE,pthread_once
.weakref    _ZL27__gthrw_pthread_getspecificj,pthread_getspecific
.weakref    _ZL27__gthrw_pthread_setspecificjPKv,pthread_setspecific
.weakref    _ZL22__gthrw_pthread_createPmPK14pthread_attr_tPFPvS3_ES3_,pthread_create
.weakref    _ZL20__gthrw_pthread_joinmPPv,pthread_join
.weakref    _ZL21__gthrw_pthread_equalmm,pthread_equal
.weakref    _ZL20__gthrw_pthread_selfv,pthread_self
.weakref    _ZL22__gthrw_pthread_detachm,pthread_detach
.weakref    _ZL22__gthrw_pthread_cancelm,pthread_cancel
.weakref    _ZL19__gthrw_sched_yieldv,sched_yield
.weakref    _ZL26__gthrw_pthread_mutex_lockP15pthread_mutex_t,pthread_mutex_lock
.weakref    _ZL29__gthrw_pthread_mutex_trylockP15pthread_mutex_t,pthread_mutex_trylock
.weakref    _ZL31__gthrw_pthread_mutex_timedlockP15pthread_mutex_tPK8timespec,pthread_mutex_timedlock
.weakref    _ZL28__gthrw_pthread_mutex_unlockP15pthread_mutex_t,pthread_mutex_unlock
.weakref    _ZL26__gthrw_pthread_mutex_initP15pthread_mutex_tPK19pthread_mutexattr_t,pthread_mutex_init
.weakref    _ZL29__gthrw_pthread_mutex_destroyP15pthread_mutex_t,pthread_mutex_destroy
.weakref    _ZL30__gthrw_pthread_cond_broadcastP14pthread_cond_t,pthread_cond_broadcast
.weakref    _ZL27__gthrw_pthread_cond_signalP14pthread_cond_t,pthread_cond_signal
.weakref    _ZL25__gthrw_pthread_cond_waitP14pthread_cond_tP15pthread_mutex_t,pthread_cond_wait
.weakref    _ZL30__gthrw_pthread_cond_timedwaitP14pthread_cond_tP15pthread_mutex_tPK8timespec,pthread_cond_timedwait
.weakref    _ZL28__gthrw_pthread_cond_destroyP14pthread_cond_t,pthread_cond_destroy
.weakref    _ZL26__gthrw_pthread_key_createPjPFvPvE,pthread_key_create
.weakref    _ZL26__gthrw_pthread_key_deletej,pthread_key_delete
.weakref    _ZL30__gthrw_pthread_mutexattr_initP19pthread_mutexattr_t,pthread_mutexattr_init
.weakref    _ZL33__gthrw_pthread_mutexattr_settypeP19pthread_mutexattr_ti,pthread_mutexattr_settype
.weakref    _ZL33__gthrw_pthread_mutexattr_destroyP19pthread_mutexattr_t,pthread_mutexattr_destroy
.ident  "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-3)".section    .note.GNU-stack,"",@progbits

0

Решение

Диспетчеризация виртуальных методов полностью зависит от реализации (т.е. специфичный для компилятора). Стандарт ничего не говорит о том, как это должно быть реализовано, это может быть использование VTables, черная магия или кто знает что еще.

При этом, мы можем только догадываться, что происходит …

что заставляет одного быть больше другого? Где в виртуальной таблице, что делает дочерний класс больше, чем тестовый класс?

Если предположить, ваш компилятор на самом деле использует VTables, обычный способ их реализации состоит в том, чтобы добавить скрытый указатель на VTable для каждого объекта тогда и только тогда, когда у класса есть виртуальные методы (независимо от того, являются ли они обычными методами или деструктором класса). Так что ваши parent класс на самом деле будет выглядеть так под капотом:

class parent {
VTable* hidden_vtable_ptr; // this is likely to be why child is bigger than test
int glove;
public:
parent() {}
virtual ~parent() {}
};

Обратите внимание, как это происходит, используете ли вы виртуальный деструктор или просто виртуальный метод:

struct test_pod {
int t[2];
};
struct test_dtor {
int t[2];
virtual ~test_dtor() {}
};
struct test_method {
int t[2];
virtual void foo() {}
};

int main() {
std::cout << sizeof(test_pod) << std::endl;    // 8
std::cout << sizeof(test_dtor) << std::endl;   // 16
std::cout << sizeof(test_method) << std::endl; // 16
return 0;
}
1

Другие решения

Он просто добавляет указатель на виртуальную таблицу в ваш дочерний класс, который оказывается 32-разрядным на вашей конкретной платформе.

1

По вопросам рекламы [email protected]