変数のスコープとモジュール分割
久方ぶりにC++の仕事で、浦島太郎状態。
プログラムが少しずつ大きくなってきたが、ソースファイルを分割するにはどうしたら良かったんだっけ?
忘れていた事を、まずはメモ。
変数のスコープ
まずは、簡単なプログラムから。
と、ここでエラー。(11行目)そうか、cは前方参照なんだったけ。
JavaやC#を使っていると、こんな事まで、まちがってしまう。
プロトタイプ宣言が必要だったんだ。
main関数の前にプロトタイプ宣言を追加、ついでに変数sの宣言も移動。
このプログラムをリファクタリング、func1を別モジュールに移動、モジュール分割してみる。
コンパイルしたら「error C2065: 's' : 定義されていない識別子です。」と、VC++に怒られた。
そうだ、main.cppに別モジュールで宣言されている変数を認識させるにはextern宣言が必要なんだった。
リスト4・リスト3-2の4行目にextern宣言を追加。(Sub.cpp)
でも、本来、extern宣言とかプロトタイプ宣言は、ヘッダーファイルに記述すべきでは。
という事で新たにヘッダsub.hを追加。
2重includeを防止するためには、インクルードガードも追加。
main.cppにsub.hをinclude。
でも、インクルードガードだと、識別子_SUB_Hが他のファイルに宣言されている識別子と名前がカブってしまう可能性があるので、 コンパイラが「#pragma once」をサポートしているならこっちを使った方が良いかな。
「#pragma once」は最近のコンパイラ(VCやgcc)でサポートされているらしい。
subモジュールだけで使いたいグロ-バル変数はstatic宣言で
リスト4では変数sをextern宣言する事でmain.cppとsub.cppで同じ変数として認識させる事ができたが、 今度はその逆に、そのモジュール内だけで使える別の変数として認識させるにはどうしたら良いだろうか?
それを実現させようと思って、例えば、以下のようなコードにするとコンパイルエラーになってしまう。
(VS2012のVC++では「error LNK2005: "char * s" は既に main.obj で定義されています。」とか「error LNK1169: 1 つ以上の複数回定義されているシンボルが見つかりました。」というエラーになってしまう。)
こんなときは、変数sをstaticに宣言することで解決できる。
ここまでは、c言語でも同じであるが、そう言えばC++では名前空間も使えたんだっけ。
名前空間の活用
という事で変数sの宣言を名前空間で囲ってしまって、変数sにスコープ解決演算子「::」を使って名前空間を指定すればコンパイルエラーは起きなくなる。
名前空間はJavaのパッゲージに相当する。
C#でも名前空間と呼んでいる。
main関数は名前空間の外(グローバル名前空間)で定義する。
マクロ定義もグローバル名前空間に入れられない。
名前空間はネスト可能。
名前空間はエイリアスが可能
無名名前空間(匿名名前空間)というのもある。
using宣言とusingディレクティブとがある。
参考URL
- Programming Place Plus C言語編 第23章 複数ファイルによるプログラム
- 名前空間とヘッダファイル
- C言語の正しいヘッダファイルの書き方 - saito’s blog
- インクルードガード
- C++編(言語解説) 第19章 静的メンバ
- C++名前空間
- C++編(言語解説) 第18章 名前空間