Вот история:
Я занимаюсь разработкой программного обеспечения C ++ для процессора ARM Cortex-M0 в Linux с помощью AC6 Toolpack. До того, как я использовал Keil (в Windows) (у которого есть свой собственный набор инструментов), я перешел на GNU-toolchain ((Инструменты GNU для встроенных процессоров ARM) 5.2.1). Первое, что я понял, это; размер двоичного файла существенно увеличился. Я проверил каждую оптимизацию компилятора (кроме оптимизации времени компоновки, она выдает ошибку во встроенной сборке, не является частью вопроса, но может быть связана с ответом). Затем начал проверять исполняемые файлы (elf-файл, а не bin, gnu создает оба) любым доступным инструментом: objdump, readelf, nm. Я нашел некоторые символы, вызывающие увеличение размера, значимые из них:d_print_comp_inner
‘,’d_exprlist
‘,’d_template_args
». Но понятия не имею, что вызывает появление этих функций в двоичном виде. (я использовал минимальные библиотеки: nano newlib). Короче говоря, я начал устранять коды один за другим, чтобы найти виновника. Наконец-то это была декларация абстрактного метода!
Определение функции как
virtual Return_type function_name(...)=0;
вместо
virtual Return_type function_name(...);
добавив 45 КБ и символы, которые я упомянул. И это единственное изменение в исходном коде. Пустое определение в базовом классе присутствует. Обратите внимание: метод все еще является виртуальным и переопределяется в дочерних классах.
Размер вывода без абстрактного класса:
text data bss dec hex filename
15316 24 4764 20104 4e88 temc_discovery.elf
Размер вывода с абстрактным классом:
text data bss dec hex filename
61484 128 4796 66408 10368 temc_discovery.elf
Здесь символы и их размеры, которые появляются, когда метод абстрактный, исключены те, которые появляются в обеих версиях. (nm
инструмент используется. Не полный список, с размером> = 0x60)
00002de4 t d_print_comp_inner
00001a34 t d_exprlist
00000ca4 t d_template_args
00000678 t d_type
00000574 t d_print_mod
000003f8 t d_encoding
000003e0 r cplus_demangle_operators
000003c8 t d_expression_1
000003a8 t d_name
00000354 t d_demangle_callback.constprop.15
000002e0 t d_print_mod_list
00000294 r cplus_demangle_builtin_types
00000268 t d_unqualified_name
00000244 T _printf_i
00000238 t d_print_function_type.isra.11
000001fc T _svfprintf_r
000001fc T _svfiprintf_r
000001f4 t d_print_array_type.isra.10
000001ce t d_print_cast.isra.12
0000018c t d_substitution
00000110 t d_operator_name
0000010c T __sflush_r
000000e8 T __swsetup_r
000000e6 t d_cv_qualifiers
000000e0 t d_print_subexpr
000000e0 t d_expr_primary
000000dc T _printf_common
000000cc T __cxa_demangle
000000c8 t d_source_name
000000c4 r standard_subs
000000c4 T __ssputs_r
000000b0 T __swbuf_r
000000ac T _malloc_r
000000a8 T _fputs_r
000000a4 T __smakebuf_r
000000a0 T __gnu_cxx::__verbose_terminate_handler()
00000096 t d_print_expr_op
0000008c T _free_r
0000008c t d_parmlist
0000008a t d_growable_string_callback_adapter
0000007c T __sfp
00000072 t d_append_buffer
00000068 T __sinit
00000060 d impure_data
Некоторые имена, знакомые мне (например, printf, flush, malloc, fputs и т. Д.), Даже не упоминаются в исходном коде.
Кто-нибудь с какой-либо идеей, что вызывает такое поведение?
Обновить:
Я уже отключал исключения с флагом --noexception
так что я не дал ни одного, хотя на это. Как выясняется, так приятно ответить здесь, упомянуть об этом здесь.
Обновление 2:
Это самый полный сайт все объясняю, если вы отслеживаете ссылки в ответах.
Это почти наверняка из-за неожиданного включения обработки исключений, встроенной в нее libc ++, независимо от того, компилируете ли вы свой код или нет --noexception
или каким бы ни был правильный гнуизм.
Исключением является, вероятно, «чистый вызов виртуальной функции» или что-то в этом роде (довольно непонятная ошибка времени выполнения, которую можно получить, но это возможно, если вы вызываете виртуальные функции в конструкторе базового класса).
Ответ заключается в том, чтобы предоставить собственную пустую реализацию этого, atexit () и любой случайный вызов, который вам не нужен. Как только вы это сделаете, компоновщик не будет перетаскивать другие вещи (которые перетаскивают другие вещи, которые перетаскивают другие вещи и т. Д.).
void __cxa_pure_virtual(void)
{
BKPT();
}
Это то, что у меня есть в нашем проекте, хотя в вашей версии libc ++ все могло измениться
Насколько я понимаю, когда вы делаете свою виртуальную функцию в базовом классе чистый Вы создаете потенциал для совершения чисто виртуального звонка. Таким образом, компилятор генерирует код, в котором он печатает сообщение о чистом факте виртуального вызова, разделенных именах для функций и классов и может быть чем-то другим. Для этого он добавляет кучу функций в ваш двоичный файл, поэтому размер увеличивается.
Я предлагаю добавить пустую реализацию к вашей чисто виртуальной функции — может быть, это мешает компилятору делать такие вещи.