質問: bc; } private: static int id; // オブジェクト生成時に増加させる為、静的 int mID; // 一意に識別する為のID int mType; // オブジェクトのタイプ Vector3 mPosition; // 位置 BoundingCircle mBoundingCircle; // 当たり判定 }
回答: > > 例えば、下記のような場合、Vector3、BoundingCircleは実体で持つべきでしょうか。ポインタで持つべきでしょうか。 > > > この例であれば「実体で持つ」、つまり質問中コードの通り値型データメンバで保持することをおすすめします。 **設計の観点:** 位置(`Vector3`)や当たり判定用の外接円?(`BoundingCircle`)といった情報は、このアプリケーション共通部品(`BaseEntity`)に強く紐づいた属性ですから、それぞれのクラス実体のラフサイクル管理を一体で捉えるべきと考えます。オブジェクト指向設計的には “has-a” 関係として説明されるものです。 **実装の観点:** 名前と説明文からの推測ですが、`Vector3`や`BoundingCircle`は2~3個の数値型から構成される小さなクラス(構造体)と予想されます。このようなデータ構造を保持する場合、動的確保+ポインタ構造ではメモリ空間を浪費し、ポインタを介した変数アクセス・コストが相対的に大きくなってしまます。実体として`BaseEntity`クラスに内包してしまえば、このようなオーバーヘッドは全て解消します。 いずれにせよ位置情報や当たり判定情報をprivateメンバとして保持していますから、`BaseEntity`クラスの内部実装を後から変更する(実体で持つ→ポインタで持つ)ことは、同クラスの利用者にとっては影響がありません。何らかの理由で後からポインタで保持したいとなっても、そのときに内部実装を切り替えれば十分かと思います。 > > ポインタで持つ場合の利点は、生成を遅らせることができる、 > > デメリットとしてはnullチェックが必要であることだと思っております。 > > > またnullチェックへの言及がありますが、設計上は「位置や当たり判定を持たない`BaseEntity`の存在を許すか」で判断すべきかと思います。答えがNOならばnullチェックはassert文で十分、つまりプログラムのバグ以外では気にする必要がありません。YESならば、`std::unique_ptr`などの(スマート)ポインタ型や、`std::optional`といった無効/有効値を表すデータ型の利用を検討してみてください。 > > デメリットとしてはヘッダのインクルードが増えてしまうこと、循環参照が起きる可能性があることだと思っております。 > > > (あくまで推測ですが)`Vector3`や`BoundingCircle`などのクラスは、このアプリケーション(ゲーム)を構成するネジ・クギのような非常にプリミティブな共有部品のはずです。このような部品を宣言するヘッダで循環参照が起きるということは考えにくいですし、もし起きてしまうようならヘッダファイル構成を再考すべきでしょう。