- ベストアンサー
宣言と定義 通用範囲やインスタンス
C++ビギナーの者です。 オブジェクト指向プログラムを見よう見まねでやって壁にぶつかっています。 わからない事が3つ程あります。 1. 「ヘッダーファイル中」と「ソースファイルの、関数・クラスの外側」と「その中」 この3箇所での、宣言や定義にはどんな制限等が存在するのでしょうか? ヘッダーに書くとダメなものが、ソースでならOKだったり混乱しています。 2. ClassAというクラスのインスタンスを、「複数の関数」で使いたい場合に 「ソースファイルの、関数・クラスの外側」 で ClassA classa; 等として、ソース内の複数の関数で利用するのは大間違いなのでしょうか? グローバル変数が何度も初期化されるトラブル等が起きています。 3. ソースファイルA側でデータを入れたvectorを ソースファイルB側で使うにはexternを付けるだけで良いのでしょうか? 手元にある参考資料から、見よう見まねでやってしまってるのですが、 C++の解説サイトにはあまり、こういった情報が載ってないのか見つけられません。 やはり書籍を買って勉強すべきだと痛感し始めたのですが、 解説のあるWebサイト、おすすめの書籍などが有りましたら それらも教えて頂けると幸いです。 よろしくお願いします っ_ _)っ
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
実際にコンパイルしたオブジェクトコードに含まれるシンボルなどをダンプしてみると、分かりやすいのですが。 >1. >「ヘッダーファイル中」と「ソースファイルの、関数・クラスの外側」と>「その中」 >この3箇所での、宣言や定義にはどんな制限等が存在するのでしょうか? >ヘッダーに書くとダメなものが、ソースでならOKだったり混乱していま >す。 ヘッダファイルに書いた宣言は、インクルードしたソースでのみ展開されますから、例えば int i; という宣言があった場合、インクルードしたソースでは常にこの変数を実装していることになります。 いわゆるグローバル変数というやつですね。 通常ヘッダファイルはifdefなどで、2重読み込みされないよう配慮されていますから、同じヘッダファイルをインクルードしているソース中に1つだけ宣言がされていることになります。 一方ソース中の関数の外、これもグローバル変数ですが、ヘッダファイルのように2重読み込みされないような配慮がされていないため、複数のソースでint iという宣言が出現してしまう可能性があります。 (※実はこの辺はソースの書き方などをプログラマがきちんと配慮してるかどうかにも依存します。) この場合、コンパイルした後、リンクする際に、Dupliacted Symbolsとかってエラーが出る場合があります(古い記憶なので、微妙かも)。 ↑の場合はexternという予約後をつけて宣言することで、どこか別の場所で宣言されている同名の変数を実体として参照する...のように宣言させることも可能です。 一方、関数の中は、常に関数スタックで参照される変数なので、関数が呼ばれるたびに作成されるため、それぞれの関数内でのみ有効です。 故にローカル変数と言います。 >2. >ClassAというクラスのインスタンスを、「複数の関数」 >で使いたい場合に > 「ソースファイルの、関数・クラスの外側」 >で ClassA classa; >等として、ソース内の複数の関数で利用するのは大間違 >いなのでしょうか? >グローバル変数が何度も初期化されるトラブル等が起きています。 何度も初期化される、ということはインスタンスが複数できているハズです。 デバッガなどで、インスタンスのアドレスを確認すると、恐らく異なるアドレスが返ってくるかと思います。 C++ではstatic をつけて宣言することで、常にインスタンスは1つしかないことが保障されます。 >3. >ソースファイルA側でデータを入れたvectorを >ソースファイルB側で使うにはexternを付けるだけで良いのでしょうか? ソースファイルA側のグローバルクラス(STLですよね?)のインスタンスを参照する場合であれば、externを使います。 そもそもstdoutとかstderrとかが一番身近なところにあるexternされている変数と考えれば、分かりやすいのではないでしょうか? ※興味半分、結構思いだしながら書いてるので、きちんと確認していただくことをお薦めします。
その他の回答 (2)
- zwi
- ベストアンサー率56% (730/1282)
そもそもオブジェクト指向化を目指して組んでいるのに、グローバル変数をクラスが初期化する段階で変な気が。クラスのインスタンスは複数作成できますが、それぞれのインスタンスは違う値を持つことと出来る=別のデータとして独立して扱えるように組む必要があります。同じグローバル変数を初期化している段階でインスタンスとして問題があります。 グローバルに参照可能な1つのクラスのインスタンスが値を保持するならまだ分りますが。 いくつか参考サイトを紹介しておきます。 http://www5c.biglobe.ne.jp/~ecb/cpp/cpp00.html http://www.asahi-net.or.jp/~yf8k-kbys/newcpp0.html http://www.geocities.jp/cbc_vbnet/vc/CV_main.html http://wisdom.sakura.ne.jp/programming/cpp/index.html
お礼
いえ、クラスで初期化していないのに 上記の使い方で初期化が起きて困惑していました。 #1様の助言でおそらく解決できそうです。ありがとうございます。 そしてリンクもありがとうございます。 解説サイト毎に違った側面で理解できるので 出来る限りすべて一度目を通して、もっと基礎を固めようと思います。 ありがとうございました
- Tacosan
- ベストアンサー率23% (3656/15482)
ぶっちゃけて言えば「ヘッダファイル」とか「ソースファイル」とかいうのは「人間がそう思っている」だけであり, 処理系からしてみればいずれも「ソースファイル」でいうことになります. だから, 本質的には「ヘッダファイルの中」とか「ソースファイルの関数・クラスの外側」などというのは意味のある区別ではありません. で, 記憶領域を確保する定義は「プログラム全体で 1つ」である必要があります. 一方, 記憶領域を確保しない定義や定義でない宣言は, 「同じもの」であれば 1つのコンパイル単位に何回現れてもよいことになっています. ということで, まずは「定義」と「(定義でない) 宣言」をしっかり区別することをお勧めします. 少なくとも変数についていえば「extern があれば宣言, なければ定義」でだいたいなんとかなる.
お礼
なるほど。 何か分けると別物のように思えていましたが、同じなのですね。 >記憶領域を確保する定義は「プログラム全体で 1つ」である必要 >記憶領域を確保しない定義や定義でない宣言は, >「同じもの」であれば 1つのコンパイル単位に何回現れてもよいことに この節をしっかり理解できないという事は、やはり 私のこのあたりの認識が悪いのですね。 もう1度しっかり調べなおして学習してきます。 >「extern があれば宣言, なければ定義」でだいたいなんとか externからそのまま宣言定義を1行で済ますのはまずかった・・のでしょうか; 何はともあれ、この先為になりそうな助言、ありがとうございました!
お礼
>1 なるほど。 ヘッダーは2重読込み防止があり、 ソース側はそれが無い事が影響する事が有るのですね。 ありがとうございますっ 頭に叩き込んでおきます。 ローカルに関しては習った通りのようで安心しました。 >2 そんな事が起きていたのですね。 ループ処理が1回ループを抜けると デバッグでは再突入してくれないので、確認は出来ないのですが、 staticで1つに出来るのですね。 値がちゃんと入ったグローバル変数が初期化されて 訳がわからなくなっていて、本当に助かりました・・っ! >3 大丈夫なようですね。 ありがとうございます。 沢山のご助言本当にありがとうございます。 たまっていた疑問が一気に吹き飛びました。感謝!