変数のスコープとモジュール分割


久方ぶりにC++の仕事で、浦島太郎状態。

プログラムが少しずつ大きくなってきたが、ソースファイルを分割するにはどうしたら良かったんだっけ?

忘れていた事を、まずはメモ。


変数のスコープ

まずは、簡単なプログラムから。

リスト1

と、ここでエラー。(11行目)

そうか、cは前方参照なんだったけ。

JavaやC#を使っていると、こんな事まで、まちがってしまう。

プロトタイプ宣言が必要だったんだ。

main関数の前にプロトタイプ宣言を追加、ついでに変数sの宣言も移動。

リスト2

このプログラムをリファクタリング、func1を別モジュールに移動、モジュール分割してみる。

リスト3-1(Main.cpp)

リスト3-2(Sub.cpp)

コンパイルしたら「error C2065: 's' : 定義されていない識別子です。」と、VC++に怒られた。

そうだ、main.cppに別モジュールで宣言されている変数を認識させるにはextern宣言が必要なんだった。

リスト4・リスト3-2の4行目にextern宣言を追加。(Sub.cpp)

でも、本来、extern宣言とかプロトタイプ宣言は、ヘッダーファイルに記述すべきでは。

という事で新たにヘッダsub.hを追加。

2重includeを防止するためには、インクルードガードも追加。

リスト5(sub.h)

main.cppにsub.hをinclude。

リスト6(main.cpp)

でも、インクルードガードだと、識別子_SUB_Hが他のファイルに宣言されている識別子と名前がカブってしまう可能性があるので、 コンパイラが「#pragma once」をサポートしているならこっちを使った方が良いかな。

「#pragma once」は最近のコンパイラ(VCやgcc)でサポートされているらしい。

リスト7(sub.h)

subモジュールだけで使いたいグロ-バル変数はstatic宣言で

リスト4では変数sをextern宣言する事でmain.cppとsub.cppで同じ変数として認識させる事ができたが、 今度はその逆に、そのモジュール内だけで使える別の変数として認識させるにはどうしたら良いだろうか?

それを実現させようと思って、例えば、以下のようなコードにするとコンパイルエラーになってしまう。

(VS2012のVC++では「error LNK2005: "char * s" は既に main.obj で定義されています。」とか「error LNK1169: 1 つ以上の複数回定義されているシンボルが見つかりました。」というエラーになってしまう。)

リスト8-1(main.cpp)

リスト8-2(sub.cpp)

こんなときは、変数sをstaticに宣言することで解決できる。

リスト9

ここまでは、c言語でも同じであるが、そう言えばC++では名前空間も使えたんだっけ。

名前空間の活用

という事で変数sの宣言を名前空間で囲ってしまって、変数sにスコープ解決演算子「::」を使って名前空間を指定すればコンパイルエラーは起きなくなる。

リスト10

名前空間はJavaのパッゲージに相当する。

C#でも名前空間と呼んでいる。

main関数は名前空間の外(グローバル名前空間)で定義する。

マクロ定義もグローバル名前空間に入れられない。

名前空間はネスト可能。

名前空間はエイリアスが可能

無名名前空間(匿名名前空間)というのもある。

using宣言とusingディレクティブとがある。

参考URL

 

Cの参考書 /  C++の参考書 /  eclipseの参考書 /  makeの参考書 / 

 

ページのトップへ戻る