Double selection of constructor characters

Today I discovered a rather interesting thing about g++ or nm ... constructor definitions seem to have two entries in the libraries.

I have a thing.hpp header:

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

And thing.cpp :

 #include "thing.hpp" Thing::Thing() { } Thing::Thing(int x) { } void Thing::foo() { } 

I will compile this with:

 g++ thing.cpp -c -o libthing.a 

Then I ran nm on it:

 %> 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 

As you can see, both constructors for Thing are listed with two entries in the generated static library. My g++ is 4.4.3, but the same behavior happens in clang , so this is not just a gcc problem.

This does not cause any obvious problems, but I was wondering:

  • Why are certain constructors listed twice?
  • Why does this not cause a problem with "multiple __ character definitions"?



EDIT : for Carl, output without argument C :

 %> nm -g libthing.a 0000000000000030 T _ZN5Thing3fooEv 0000000000000022 T _ZN5ThingC1Ei 000000000000000a T _ZN5ThingC1Ev 0000000000000014 T _ZN5ThingC2Ei 0000000000000000 T _ZN5ThingC2Ev U __gxx_personality_v0 

As you can see ... the same function generates several characters, which is still pretty curious.

And while we're on it, here is the generated assembly section:

 .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 

So, the generated code ... well ... the same.




EDIT . To find out which constructor is actually called, I changed Thing::foo() to this:

 void Thing::foo() { Thing t; } 

Generated 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 

Thus, it calls the full constructor of the object.

+79
c ++ gcc constructor
Aug 03 '11 at 3:22
source share
1 answer

Let's start with the announcement that GCC follows Itanium C ++ ABI .




According to ABI, the garbled name for your Thing::foo() easily parsed:

 _Z | N | 5Thing | 3foo | E | v prefix | nested | `Thing` | `foo`| end nested | parameters: `void` 

You can also read the names of the constructors as shown below. Note that the "name" constructor is not specified, but instead: C :

 _Z | N | 5Thing | C1 | E | i prefix | nested | `Thing` | Constructor | end nested | parameters: `int` 



But what is this C1 ? Your duplicate has C2 . What does it mean?

Well, this is also pretty simple :

  <ctor-dtor-name> ::= C1 # complete object constructor ::= C2 # base object constructor ::= C3 # complete object allocating constructor ::= D0 # deleting destructor ::= D1 # complete object destructor ::= D2 # base object destructor 



Wait, why is it that simple? This class has no base. Why does he have a “constructor of objects” and a “constructor of the base object” for each?

  • This Q&A implies that it is simply a byproduct of supporting polymorphism, although in this case it is not actually required.

  • Note that c++filt used to include this information in its unmounted output, but no longer exists .

  • This forum post asks the same question, and the only answer does not help to answer it better, except that GCC can avoid emitting two constructors when polymorphism is not involved, and that this behavior should be improved in the future.

  • This newsgroup post describes a problem with setting breakpoints in designers because of this double radiation. He reiterated that the root of the problem was support for polymorphism.

In fact, this is indicated as a “known issue” by GCC :

g ++ emits two copies of constructors and destructors.

In the general case, there are three types of constructors (and destructors).

  • The full constructor / destructor of the object.
  • Constructor / destructor of the base object.
  • Assigning constructor / deallocating destructor.

The first two are different when virtual base classes are involved.




The meaning of these different constructors is as follows:

  • "Complete constructor of objects." It additionally creates virtual base classes.

  • "Constructor of the base object." It creates the object itself, as well as data elements and non-virtual base classes.

  • "Purpose of the constructor of objects." It does everything that the full constructor of objects does, and also calls a new operator to actually allocate memory ... but, apparently, this is usually not visible.

If you do not have virtual base classes, [the first two] are identical; GCC will, at sufficient optimization levels, actually pseudonymize characters for the same code for both.

+110
Aug 03 2018-11-11T00:
source share



All Articles