undefined reference to 'Class::member' の原因と直し方(staticメンバ変数)
クラス内でstaticメンバ変数を宣言しただけで、クラスの外側での実体定義を書き忘れたときにリンクの段階で出るエラーです。コンパイル自体は通りますが、実行ファイルを作る最後のリンク工程で失敗します。
このエラーが出る典型パターン
パターン1
1 #include <iostream> 2 class Counter { 3 public: 4 static int total; 5 Counter() { total++; } 6 }; 7 ^ 8 int main() { 9 Counter a, b; 10 std::cout << Counter::total << std::endl; 11 return 0; 12 }
undefined reference to `Counter::total'
クラス内のstatic int total;はあくまで宣言です。実体(メモリ上の場所)はクラスの外側で一度だけ定義する必要があり、忘れるとコンパイルは通ってもリンクで失敗します。
直し方: (空) を int Counter::total = 0; にします。
広告
広告スロット(未設定)
パターン2
1 #include <iostream> 2 class Logger { 3 public: 4 static int count; 5 Logger() { count++; } 6 }; 7 ^ 8 int main() { 9 Logger a, b, c; 10 std::cout << Logger::count << std::endl; 11 return 0; 12 }
undefined reference to `Logger::count'
エラーはコンパイラ(g++)ではなくリンカ(ld)から出ます。コンパイル自体は各行が文法的に正しいので通ってしまいます。
直し方: (空) を int Logger::count = 0; にします。
パターン3
1 #include <iostream> 2 class Id { 3 public: 4 static int next; 5 Id() { id = next; next++; } 6 int id; 7 }; 8 ^ 9 int main() { 10 Id a, b; 11 std::cout << b.id << std::endl; 12 return 0; 13 }
undefined reference to `Id::next'
staticメンバ変数の実体定義はクラス定義の外側、通常はグローバルスコープに1行書くだけで済みます。
直し方: (空) を int Id::next = 0; にします。
よくある誤解
「クラスの中に書けばそれで変数が実在する」というのは誤解です。クラス内のstaticメンバ変数の宣言はあくまで宣言であり、実体(メモリ上の場所)はクラスの外側で一度だけ定義する必要があります。忘れるとコンパイルは通ってもリンクで失敗します。
まとめ
undefined reference to 'Class::member'(staticメンバ変数)は中級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。