質問: C++初心者です。複数のファイルを取り込んだプログラムがコンパイルできず、その理由もわかりません。 A.cpp ``` #include "B.h" int main(int argc, char **argv) { B test; test.show(*argv); return 0; } ``` B.h ``` #pragma once #ifndef __B_H__ #define __B_H__ #include class B { public: void show(std::string); }; #endif ``` B.cpp ``` #include "B.h" #include #include using namespace std; class B { public: void show(const char *var) { cout << var << endl; } }; ``` これでコンパイル ``` g++ -o a.exe A.cpp ``` すると ``` /tmp/ccGn41oP.o:A.cpp:(.text+0x4c): `B::show(std::string)' に対する定義されていない参照です /tmp/ccGn41oP.o:A.cpp:(.text+0x4c): 再配置がオーバーフローしないように切り詰められました: R_X86_64_PC32 (未定義シンボル `B::show(std::string)' に対して) collect2: エラー: ld はステータス 1 で終了しました ``` こうなってしまいます。 これはどういうエラーなんでしょうか。 こんな初歩的な質問で恐縮ですが、教えてください。 > > ご回答について > > > ご回答ありがとうございます。 コンパイルの流れを理解していませんでした。 おっしゃる通り、 ``` g++ -c B.cpp ``` を実行したところ、 ``` $ g++ -c B.cpp B.cpp:8:7: エラー: ‘class B’ が再定義されています class B { ^ In file included from B.cpp:1:0: B.h:8:7: エラー: ‘class B’ の前の定義 class B { ^ ``` となってしまったため、プロトタイプの実装本体はクラス宣言しないのかと思い、 下記に変更して再度実行しました。 B.cpp ``` #include "B.h" #include #include using namespace std; void show(const char *var) { cout << var << endl; } ``` これで"B.o"というファイルが生成され、改めて ``` g++ -o A.exe A.cpp ``` を実行したものの、エラーの内容が変わりませんでした。 > > sayuri さん > > > ちなみにですが・・・ 引数を ``` void show(std::string var); ``` に統一し、 ``` g++ -c B.cpp g++ -o A.exe A.cpp ``` を行うと ``` $ g++ -o a.exe A.cpp /tmp/ccSVMoNA.o:A.cpp:(.text+0x4c): `B::show(std::string)' に対する定義されていない参照です /tmp/ccSVMoNA.o:A.cpp:(.text+0x4c): 再配置がオーバーフローしないように切り詰められました: R_X86_64_PC32 (未定義シンボル `B::show(std::string)' に対して) collect2: エラー: ld はステータス 1 で終了しました ``` というエラーになります。 このブログを見ると、この場合はtemplate関数の場合ですが、 「この関数は使われていないから.objを生成しても実態がないのでA.cppからは実装が見えません」 というようなことが書いてありました。 もしこれと同様の現象が起きているのだとすると、今回show()関数はグローバルメンバ関数ではなくクラスメンバ関数なのでA.cppから見ることはできない ということになってしまうんでしょうか。 言語仕様をほとんどよくわかっていないので、さすがにそんなことはないんじゃないかと思いますが・・・ C++開発者の方も、実行ファイル1つで完結するプログラムなんてそうそう書かないと思いますが、 皆さんはどうやって外部関数を参照しているのかわかりません。
回答: C言語及びC++言語には宣言と定義の2つが存在します。 B.hに ``` class B { public: void show(std::string); }; ``` と宣言した場合、B.cppに記述すべきこれに対応する定義は ``` void B::show(std::string){ ... } ``` となります。C言語と異なりC++言語には関数オーバーロードがあるため、引数を完全に一致させる必要があります。また `void B::show(std::string)` と `void show(std::string)` も別関数です。