Двойное выделение символов конструктора

Сегодня я обнаружил довольно интересную вещь о g++ или nm … определения конструктора, по-видимому, имеют две записи в библиотеках.

У меня есть заголовок thing.hpp :

 class Thing { Thing(); Thing(int x); void foo(); }; 

И thing.cpp :

 #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 

Таким образом, он вызывает полный конструктор объекта.

Мы начнем с объявления о том, что 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 будет на достаточных уровнях оптимизации фактически называть символы одинаковым кодом для обоих.

  • Каково использование статических конструкторов?
  • Конструктор абстрактного classа в C #
  • Что на самом деле делает «новое» ключевое слово на Java, и не следует ли мне создавать новые объекты?
  • тривиальный или стандартный макет против POD
  • Могут ли конструкторы исключать исключения из Java?
  • вызов чистой виртуальной функции из конструктора базового classа
  • Как инициализируется std :: cout?
  • C # конструктор цепочки? (Как это сделать?)
  • Сеттерные методы или конструкторы
  • Можно ли использовать Class.newInstance () с аргументами конструктора?
  • Почему нельзя синхронизировать конструкторы Java?
  • Давайте будем гением компьютера.