[解決済み] コンストラクタ記号のデュアルエミッション
質問
今日、私はどちらかについて、ちょっと面白いことを発見しました。
g++
または
nm
...コンストラクタの定義はライブラリに2つのエントリがあるように見えます。
私は、ヘッダ
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
の両方のコンストラクタが、生成された静的ライブラリに 2 つのエントリでリストされていることがわかります。 私の
g++
は 4.4.3 ですが、同じ挙動が
clang
であるため、単に
gcc
の問題ではありません。
これは明らかな問題を引き起こさないのですが、私は不思議に思っていました。
- 定義されたコンストラクターが 2 回リストされているのはなぜですか?
- なぜこれは "シンボル __" の多重定義の問題を引き起こさないのですか?
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;
}
生成されたアセンブリは
.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`
以下のように、コンストラクタの名前も同様に読むことができます。コンストラクタの "name"が与えられていないことに注意してください。
C
節を使用していることに注意してください。
_Z | N | 5Thing | C1 | E | i
prefix | nested | `Thing` | Constructor | end nested | parameters: `int`
しかし、これは何だ?
C1
? あなたの複製には
C2
. これは何ですか?
の意味は
?
まあ。 も非常にシンプルです。 :
<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
待って、なぜこの シンプル ? このクラスにはベースがありません。なぜ完全なオブジェクトのコンストラクタがあるのですか? と と、それぞれに "ベースオブジェクトのコンストラクタ" があるのはなぜですか?
-
この Q&A は、この場合実際には必要でないにもかかわらず、これが単にポリモーフィズムサポートの副産物であることを私に暗示します。
-
以下のことに注意してください。
c++filt
はこの情報をデマングル出力に含んでいたことに注意してください。 に含まれていましたが、今は含まれていません。 . -
このフォーラムの投稿 は同じ質問を投げかけていますが、唯一の回答は、GCCの は 多相性が関与していないとき、GCC は 2 つのコンストラクタを生成しないようにすることができ、この動作は将来的に改善されるべきであるということを暗に示しているだけです。
-
このニュースグループへの投稿 には、このデュアル エミッションによるコンストラクタでのブレークポイントの設定に関する問題が記述されています。問題の根源はポリモーフィズムのサポートであることが改めて述べられています。
実際には は GCC の "known issue" としてリストアップされています。 :
<ブロッククオートG++はコンストラクタとデストラクタのコピーを2つ生成します。
一般に、コンストラクタ(およびデストラクタ)には3つのタイプがあります。 デストラクタ)があります。
- 完全なオブジェクトのコンストラクタ/デストラクタです。
- ベース・オブジェクトのコンストラクタ/デストラクタ。
- アロケートするコンストラクタ/デストラクタです。
最初の2つは、仮想基底クラスが含まれる場合、異なります。 が関係してきます。
これらの異なるコンストラクタの意味 は次のようなものであると思われます。 :
-
完全なオブジェクトのコンストラクタです。これはさらに仮想基底クラスを構築します。
-
基本オブジェクトのコンストラクタです。これは、オブジェクト自身、データ・メンバ、および非仮想ベース・クラスを作成します。
-
オブジェクトの割り当てを行うコンストラクタです。これは完全なオブジェクト コンストラクタが行うすべてのことを行い、さらに実際にメモリを割り当てるために演算子 new を呼び出します... が、どうやらこれは通常見られないようです。
仮想基底クラスがない場合、[最初の2つは]同一です。 GCCは、十分な最適化レベルにおいて、実際に両者に対して同じコードへのエイリアスを作成します。 を作成します。
関連
-
[解決済み】クラステンプレートの使用にはテンプレート引数リストが必要です
-
[解決済み] 数値定数の前にunqualified-idを付けて、数値を定義することを期待する。
-
[解決済み] C++で、あるコンストラクタを別のコンストラクタから呼び出す(コンストラクタ・チェイニングを行う)ことは可能ですか?
-
[解決済み] Java で、あるコンストラクタを別のコンストラクタから呼び出すにはどうすればよいですか?
-
[解決済み] C#でベースコンストラクタを呼び出す
-
[解決済み] コンストラクタ内の仮想メンバー呼び出し
-
[解決済み] あるコンストラクタを別のコンストラクタから呼び出す
-
[解決済み] ベースクラスのコンストラクタを呼び出す際のルールは?
-
[解決済み] 抽象クラスはコンストラクタを持つことができますか?
-
[解決済み] コンストラクタのシグネチャを定義するインターフェース?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] [Solved] Error C1083: Cannot open include file: 'stdafx.h'
-
[解決済み】抽象クラス型の無効なnew-expression
-
[解決済み】エラー:strcpyがこのスコープで宣言されていない
-
[解決済み】c++でstd::vectorを返すための効率的な方法
-
[解決済み】「Expected '(' for function-style cast or type construction」エラーの意味とは?
-
[解決済み】エラー。switchステートメントでcaseラベルにジャンプする
-
[解決済み] gdbを使用してもデバッグシンボルが見つからない
-
[解決済み] to_string は std のメンバーではない、と g++ が言っている (mingw)
-
[解決済み】VC++の致命的なエラーLNK1168:書き込みのためにfilename.exeを開くことができません。
-
[解決済み】Eclipse IDEでC++エラー「nullptrはこのスコープで宣言されていません」が発生する件