Двойное выделение символов конструктора
Сегодня я обнаружил довольно интересную вещь о g++
или nm
… определения конструктора, по-видимому, имеют две записи в библиотеках.
У меня есть заголовок thing.hpp
:
class Thing { Thing(); Thing(int x); void foo(); };
И thing.cpp
:
- Что такое конструктор преобразования в C ++? Для чего это?
- Может ли абстрактный class иметь конструктор?
- C ++: Когда (и как) называются глобальные статические конструкторы C ++?
- Почему абстрактные classы в Java имеют конструкторы?
- Как получить имена параметров конструкторов объекта (reflection)?
#include "thing.hpp" Thing::Thing() { } Thing::Thing(int x) { } void Thing::foo() { }
Я скомпилирую это с помощью:
g++ thing.cpp -c -o libthing.a
Затем я запускаю nm
на нем:
%> nm -gC libthing.a 0000000000000030 T Thing::foo() 0000000000000022 T Thing::Thing(int) 000000000000000a T Thing::Thing() 0000000000000014 T Thing::Thing(int) 0000000000000000 T Thing::Thing() U __gxx_personality_v0
Как вы можете видеть, оба конструктора для Thing
перечислены с двумя записями в сгенерированной статической библиотеке. Мой g++
– 4.4.3, но такое же поведение происходит в clang
, поэтому это не просто проблема gcc
.
Это не вызывает никаких очевидных проблем, но мне было интересно:
- Почему определенные конструкторы перечислены дважды?
- Почему это не вызывает проблемы «множественного определения символа __»?
EDIT : для Carl вывод без аргумента C
:
%> nm -g libthing.a 0000000000000030 T _ZN5Thing3fooEv 0000000000000022 T _ZN5ThingC1Ei 000000000000000a T _ZN5ThingC1Ev 0000000000000014 T _ZN5ThingC2Ei 0000000000000000 T _ZN5ThingC2Ev U __gxx_personality_v0
Как вы можете видеть … одна и та же функция генерирует несколько символов, что все еще довольно любопытно.
И пока мы на нем, вот раздел сгенерированной сборки:
.globl _ZN5ThingC2Ev .type _ZN5ThingC2Ev, @function _ZN5ThingC2Ev: .LFB1: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 pushq %rbp .cfi_def_cfa_offset 16 movq %rsp, %rbp .cfi_offset 6, -16 .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) leave ret .cfi_endproc .LFE1: .size _ZN5ThingC2Ev, .-_ZN5ThingC2Ev .align 2 .globl _ZN5ThingC1Ev .type _ZN5ThingC1Ev, @function _ZN5ThingC1Ev: .LFB2: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 pushq %rbp .cfi_def_cfa_offset 16 movq %rsp, %rbp .cfi_offset 6, -16 .cfi_def_cfa_register 6 movq %rdi, -8(%rbp) leave ret .cfi_endproc
Таким образом, сгенерированный код … ну … тот же.
EDIT : Чтобы узнать, какой конструктор действительно вызван, я изменил Thing::foo()
на это:
void Thing::foo() { Thing t; }
Сгенерированная assembly:
.globl _ZN5Thing3fooEv .type _ZN5Thing3fooEv, @function _ZN5Thing3fooEv: .LFB550: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 pushq %rbp .cfi_def_cfa_offset 16 movq %rsp, %rbp .cfi_offset 6, -16 .cfi_def_cfa_register 6 subq $48, %rsp movq %rdi, -40(%rbp) leaq -32(%rbp), %rax movq %rax, %rdi call _ZN5ThingC1Ev leaq -32(%rbp), %rax movq %rax, %rdi call _ZN5ThingD1Ev leave ret .cfi_endproc
Таким образом, он вызывает полный конструктор объекта.
- Почему конструкторы не могут быть унаследованы в java?
- конструктор в качестве делегата - возможно ли это на C #?
- Конструкторы Java
- Почему нет вызова конструктору?
- Конструктор UserControl с параметрами в C #
- Конструктор Java не компилируется должным образом
- Есть ли неявный конструктор по умолчанию в C ++?
- Почему конструктор копирования принимает свой параметр по ссылке в C ++?
Мы начнем с объявления о том, что GCC следует за Itanium C ++ ABI .
Согласно ABI, искаженное имя для вашей Thing::foo()
легко анализируется:
_Z | N | 5Thing | 3foo | E | v prefix | nested | `Thing` | `foo`| end nested | parameters: `void`
Вы также можете прочитать имена конструкторов, как показано ниже. Обратите внимание, что конструктор «имя» не указан, а вместо этого: предложение C
:
_Z | N | 5Thing | C1 | E | i prefix | nested | `Thing` | Constructor | end nested | parameters: `int`
Но что это такое C1
? Ваш дубликат имеет C2
. Что это значит ?
Ну, это тоже довольно просто :
::= C1 # complete object constructor ::= C2 # base object constructor ::= C3 # complete object allocating constructor ::= D0 # deleting destructor ::= D1 # complete object destructor ::= D2 # base object destructor
Подождите, почему это просто ? Этот class не имеет базы. Почему у него есть «полный конструктор объектов» и «конструктор базовых объектов» для каждого?
-
Это Q & A подразумевает, что это просто побочный продукт поддержки polymorphismа, хотя в этом случае он фактически не требуется.
-
Обратите внимание, что
c++filt
используется для включения этой информации в свой демонтированный вывод, но не более того . -
Это сообщение форума задает один и тот же вопрос, и единственный ответ не помогает лучше ответить на него, за исключением того, что GCC может избежать испускания двух конструкторов, когда polymorphism не задействован, и что это поведение должно быть улучшено в будущем ,
-
Эта публикация в группе новостей описывает проблему с установкой точек останова в конструкторах из-за этого двойного излучения. Еще раз заявлено, что корень проблемы – это поддержка polymorphismа.
Фактически, это указано как «известный вопрос» GCC :
G ++ испускает две копии конструкторов и деструкторов.
В общем случае существует три типа конструкторов (и деструкторов).
- Полный конструктор / деструктор объекта.
- Конструктор / деструктор базового объекта.
- Выделение конструктора / дезактивация деструктора.
Первые две разные, когда задействованы виртуальные базовые classы.
Смысл этих разных конструкторов заключается в следующем :
-
«Конструктор полного объекта». Он дополнительно создает виртуальные базовые classы.
-
«Конструктор базового объекта». Он создает сам объект, а также элементы данных и не виртуальные базовые classы.
-
«Назначающий объект-конструктор». Он делает все, что делает полный конструктор объектов, а также вызывает оператор new для фактического выделения памяти … но, видимо, это обычно не наблюдается.
Если у вас нет виртуальных базовых classов, [первые два] идентичны; GCC будет на достаточных уровнях оптимизации фактически называть символы одинаковым кодом для обоих.