Presentation is loading. Please wait.

Presentation is loading. Please wait.

プロジェクト演習III,V <インタラクティブ・ゲーム制作> プログラミングコース

Similar presentations


Presentation on theme: "プロジェクト演習III,V <インタラクティブ・ゲーム制作> プログラミングコース"— Presentation transcript:

1 プロジェクト演習III,V <インタラクティブ・ゲーム制作> プログラミングコース
第5回 グローバル変数とファイル分割と コンパイルの割と根本的な話

2 今日のメニュー グローバル変数の安全な使い方 ファイル分割とコンパイルの関係 リフスローの発表のフォローアップ的内容
あまり使うことをおすすめはしませんが、 必要悪な場合もあるので覚えておこう ファイル分割とコンパイルの関係 根本から理解していないと誤解を引きずるので丁寧に話します

3 嫌われがちだけど、必要な時もあるんだよ?
グローバル変数の安全な使い方

4 グローバル変数とは どの関数からでもアクセスできる変数 あまり利用は推奨されないが、 「定数」を定義する際にはよく使われる
先週のコードレビューで2つのヘッダに 分割して定義と宣言を行っていたが、 あの利用方法には若干問題がある どちらもヘッダになっているので 分離している意味が無い

5 グローバル変数もお品書き(宣言)と実体(定義)に分けよう
Global.h Global.cpp #pragma once extern const int WIDTH; extern const int HEIGHT; void procGlobalFunc(void); #include “Global.h” const int WIDTH = 800; const int HEIGHT = 600; void procGlobalFunc(void) { // 処理実体を記述 return; }

6 ポイント ヘッダでは型名の前にexternを付けて、 変数の型と名前だけを「宣言」する
実体を書くためのcppを用意し、extern宣言を書いたヘッダをインクルードしてから、 変数の実体を「定義」する 定数なら定数値の代入も行っておく 同様の手順で関数も宣言、定義が可能 関数の場合は宣言側のexternは不要

7 使用上の注意点 基本的に「定数」しか使わないのが無難 間違っても「クラスオブジェクトの実体」をグローバルに置いてはいけない!
変数をあちこちからいじるのは超危険 間違っても「クラスオブジェクトの実体」をグローバルに置いてはいけない! どうしても必要な場合はポインタをグローバルに置いて初期値をNULLにしておき、初回使用時にnewするようにして使う デザインパターンのSingletonに近い考え方

8 グローバルオブジェクト利用例 Global.h Global.cpp
#pragma once #include “GlobalHoge.h” /* ポインタを返す関数を用意して、 ダイレクトに触らせない方が良い */ //extern GlobalHoge *g_hoge; GlobalHoge* getHoge(void); #include “Global.h” GlobalHoge *g_hoge = NULL; GlobalHoge* getHoge(void) { if(g_hoge == NULL) { g_hoge = new GlobalHoge(); } return g_hoge;

9 ダメな理由 以下の状況を前提とする このプログラムを実行した時、g_aとg_bのうち、どっちが先に生成されるか?
a.cpp、b.cppにそれぞれ「HogeA g_a」と「HogeB g_b」が定義されている HogeAはHogeBの情報を利用する つまりHogeAより先にHogeBのオブジェクトが 作られていなければならない このプログラムを実行した時、g_aとg_bのうち、どっちが先に生成されるか? それは誰にも分からない

10 C++(C)のコンパイルの仕組みについて述べます
定義と宣言の分離が必要な理由

11 cppからexeまでの流れ 各cppごとにインクルードを処理します
実質的にやっているのは「コピペ」です ヘッダファイルの内容をその場所に取り込んで、結果的に「宣言」を取り込むことになります ヘッダを取り込んだcppを翻訳(コンパイル)して、中間ファイル(obj)にします 文法間違い、未宣言のクラスや変数の使用はここで「コンパイルエラー」として弾かれる 中間ファイルとライブラリを結合(リンク)して、実行ファイル(exe)にします 利用するライブラリの指定ミスや、同名の関数や変数がかち合った場合は「リンカエラー」になる

12 模式図 Hoge.h Hoge.cpp #include “Hoge.h” Fuga.h Hoge.h Hoge.h Hoge.cpp
Fuga.cpp Fuga.h #include “Hoge.h” Fuga.cpp #include “Fuga.h” コンパイル main.cpp #include “Hoge.h” #include “Fuga.h” Fuga.h Hoge.h main.cpp

13 コンパイル単位はcpp ヘッダの内容はcppにインクルードされない限り、プログラムに何も影響も与えない
インクルードが済んだ時点で、利用するクラス、変数、関数の「定義か宣言」が含まれていないとエラーになる 見えないものは使えない 複数のcppそれぞれに「宣言」が含まれるのは問題ないが、「定義」がそれぞれで行われるとリンカエラーになる

14 インクルードガードの意義 「#pragma once」と 書くか、右のようにすることで「1つのcppにおいてはそのヘッダが1回しかインクルードされない」ようにできる 2つのヘッダが同じものをインクルードしようとしている際にガードすることができる 模式図の例など 異なるcpp間での多重定義は防げない #ifndef __HOGE.H__ #ifdef __HOGE.H__ // ここにヘッダの宣言を書く #endif 「#pragma once」が使えないコンパイラ ではこちらの書き方を使う。 原理はプリプロセッサの仕組みを利用した テクニックで、「__HOGE.H__」が定義 されていなければその文字列を定義した上で 宣言を展開する。一度でも宣言されていたら それ以降はスキップする、というもの。

15 もしヘッダに「定義」が 含まれていたら? Global.h (実体付き) グローバル変数の定義をヘッダでしてしまい、それを複数のcppで取り込むと、それぞれのcppごとに別々の変数が作られてしまう そのcppの中でしか見えない変数になる 定数値(const)なら大して問題にならないが、変数やオブジェクトの場合は致命傷になる Global.h (実体付き) Fuga.h Hoge.h Hoge.h Hoge.cpp Fuga.cpp Global.h (実体付き) Fuga.h Hoge.h main.cpp

16 宣言と定義の分離 cppに定義を記述することで、コンパイルされた際に実体が生成される
Global.h (宣言) cppに定義を記述することで、コンパイルされた際に実体が生成される 他のcppからはヘッダの宣言を通じて、他のcppに記述された変数や関数、クラスを利用できる Global.h (宣言) Global.h (宣言) Fuga.h Global.cpp (実体) Hoge.h Hoge.h Hoge.cpp Fuga.cpp Global.h (宣言) Fuga.h Hoge.h main.cpp

17 意味は単一でも用途が広くて説明に困る… 謎のキーワードstaticの正体

18 static、この説明の難しきもの 一言で説明するのがとても難しい なので、代表的な目的と用途を述べるに留めます
「スコープ内に静的な変数および関数を定義し、その唯一性を保証する」じゃ分からんでしょ? なので、代表的な目的と用途を述べるに留めます

19 staticの2大用途(+1) クラスのメンバに付けて「インスタンスとは関係なく、クラスのスコープ内で共通の値を持った変数、関数を作る」時
ローカル変数に付けて「初期化は1回だけ、スコープを抜けても値が保持される変数にしたい」時 1つのcppの中だけで扱いたいグローバル変数、関数を作りたい時 externなどで外部から宣言しても扱えなくする

20 まとめ とりあえずC++では「宣言」と「定義」に分けるのがいいらしい
VisualStudioでは「ビルド」の一言で済ましているけど、実は中では様々なドラマがあるらしい extern、const、staticの用途はとりあえずバッチリ! staticは今後必要に応じて掘り下げます


Download ppt "プロジェクト演習III,V <インタラクティブ・ゲーム制作> プログラミングコース"

Similar presentations


Ads by Google