1 / 20

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

プロジェクト演習 III,V <インタラクティブ・ゲーム制作> プログラミングコース. 第 5 回 グローバル変数とファイル分割と コンパイルの割と根本的な話. 今日のメニュー. グローバル変数の安全な使い方 リフスローの発表のフォローアップ的内容 あまり使うことをおすすめはしませんが、 必要悪な場合もあるので覚えておこう ファイル分割とコンパイルの関係 根本から理解していないと誤解を引きずるので丁寧に話します. 嫌われがちだけど、必要な時もあるんだよ?. グローバル変数の安全な使い方. グローバル変数とは. どの関数からでもアクセスできる変数

eileen
Télécharger la présentation

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

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


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

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

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

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

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

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

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

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

  9. ダメな理由 • 以下の状況を前提とする • a.cpp、b.cppにそれぞれ「HogeAg_a」と「HogeBg_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)なら大して問題にならないが、変数やオブジェクトの場合は致命傷になる Fuga.h Global.h (実体付き) Hoge.h Hoge.h Hoge.cpp Fuga.cpp Global.h (実体付き) Fuga.h Hoge.h main.cpp

  16. 宣言と定義の分離 Global.h (宣言) • cppに定義を記述することで、コンパイルされた際に実体が生成される • 他のcppからはヘッダの宣言を通じて、他のcppに記述された変数や関数、クラスを利用できる Global.h (宣言) Fuga.h Global.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は今後必要に応じて掘り下げます

More Related