ページ 11

クラスのプロトタイプ宣言

Posted: 2009年10月05日(月) 00:36
by チルチル
class CA {
public:
	int a;
} A;
class CB {
public:
	int b;
} B;
こんな感じでクラスのインスタンスを作成するとAからbを使う場合は
CBとBを関数で言うプロトタイプ宣言する必要があります、可能でしょうか?

Re:クラスのプロトタイプ宣言

Posted: 2009年10月05日(月) 00:55
by ねこ
意味がよく分からないんだけどもしかしてAクラス内でBクラスを扱うって事?

それならAクラスではBクラスのポインタのみ有効です。実体の場合はBを先に定義しないといけません。
「クラス・相互参照」とか検索すると知りたい事が分かるかもしれません。

何にしてもちょっと質問の意図が分かりにくいです。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月05日(月) 01:29
by Justy

>CBとBを関数で言うプロトタイプ宣言する必要があります、可能でしょうか

 何故必要になりますか?

[color=#d0d0ff" face="monospace]
class CA
{
public:
int a;
void test();
} A;
class CB
{
public:
int b;
} B;

void CA::test()
{
B.b++;
}
[/color]

Re:クラスのプロトタイプ宣言

Posted: 2009年10月05日(月) 21:18
by チルチル
すみません例えが悪かったですね・・

例えばボスのクラスと自機のクラスを作った場合は
ホーミングなどの関係で相互に見えていないといけません

しかし先に作成した方からは後に作成した方が見えません
なのでプロタイプ宣言的な事が必要になります

冷静に考えるとJustyさんの仰るとおり
メンバ関数の実態だけ後に持って来れば良かったんですが
その場合だとクラスの作成をズラーっと書いた後に
メンバ関数の実態をズラーっと書くことになって
少し管理しづらいんですよね・・

この方法でも良いんですが
他に良い方法は無いでしょうか?

Re:クラスのプロトタイプ宣言

Posted: 2009年10月05日(月) 22:57
by Justy

>その場合だとクラスの作成をズラーっと書いた後に
>メンバ関数の実態をズラーっと書くことになって

 1つのヘッダ(と cppソース)で1つのクラス、とまでは言いませんが、
関連するクラスだけを1つにまとめればさほど管理しにくくなるとは思えないですが、
じゃぁ、CAと CBの定義順を入れ替えてはどうですか?


 ところで、このクラスはヘッダに書いているのですか?
(ヘッダに書いて複数の cppで includeするとリンクラーが出そうな気がするんですが)

Re:クラスのプロトタイプ宣言

Posted: 2009年10月05日(月) 23:18
by チルチル
>1つのヘッダ(と cppソース)で1つのクラス

今のところ選択画面以外は1つのヘッダで1つのクラスになっていますね・・

>関連するクラスだけを1つにまとめればさほど管理しにくくなるとは思えないですが

全部と言うわけではありませんがクラスは強い連鎖構造にあるので
クラスは全体の半分ぐらいのクラスと関連しています
なので無理がありますね・・

>じゃぁ、CAと CBの定義順を入れ替えてはどうですか?

あ~違います違います
CAとCBは両方とも相手のメンバを呼び出せないとダメなんです

>ところで、このクラスはヘッダに書いているのですか?

と言うより最初のクラスのコンストラクタでDxLib_Initを呼んでいて
後続のクラスのコンストラクタで読み込みを行っているので
ヘッダに書くしかないんですよね・・

cppが複数あると初期化順番が不定になるので
上記の構造を実現するためにヘッダに処理をズラーっと書いて
main.cppで初期化順に全部includeしています

Re:クラスのプロトタイプ宣言

Posted: 2009年10月05日(月) 23:45
by Justy

>CAとCBは両方とも相手のメンバを呼び出せないとダメなんです

>その場合だとクラスの作成をズラーっと書いた後に
>メンバ関数の実態をズラーっと書くことになって

 なんという・・・。

 うーん、そりゃ言語的には
[color=#d0d0ff" face="monospace]
struct internalCA
{
int a;
};
struct internalCB
{
int b;
};

class CA
{
public:
internalCA a;
void test(internalCB &cb)
{
cb.b = 1;
}
} A;

class CB
{
public:
internalCB b;
void test(internalCA &ca)
{
ca.a = 2;
}
} B;
[/color]

こんな感じに別のクラスを作って引数で渡したり、
或いはこんな感じに
[color=#d0d0ff" face="monospace]
extern int *pa, *pb;

class CA
{
public:
int a;
void test()
{
*pb = 1;
}
} A;
class CB
{
public:
int b;
void test()
{
*pb = 2;
}
} B;
int *pa = &A.a;
int *pb = &B.b;
[/color]

ポインタを経由にするとか、手はあります。

 でもさすがに目的が目的だけに、これらはとてもお勧めはできません。


 普通にメンバ関数の定義を分けて書くのがいいでしょう。


# 理想を言えば、根本的にグローバル変数に頼った、或いは相互に依存した関係に
なっている設計全般を見直した方がいいかと思います。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 01:00
by チルチル
う~んやはりメンバ関数の定義をわけるしかなさそうですね・・

今の所はグローバル変数は無いですね
まあクラスがグローバルだから同じような物ですが・・

しかし相互に依存しないで実現できるのでしょうか?
自機とボスなどは相互に依存するしかないような気がするのですが・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 01:35
by 組木紙織
別に継承を使う必要もないのですが
話の流れからこうしたいだろうと予想したコード(かなりエスパー(笑))
判定系だけ分けてあげて、自機とボスは自分の動きに関する情報だけを扱ってあげればよいと
思います。
class Kitai
{
/*機体を扱う基底クラス*/
};

class Jiki :public Kitai
{
/*
自機の位置座標、HPなどなど
*/
};


class Boss:public Boss
{
/*
Bossの位置座標などなど
*/
};


int Hantei(Kitai & kitaiA, Kitai & kitaiB)
{
/*
ここで色々判定をする。
*/
};

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 01:47
by チルチル
う~ん少し違いますね・・
判定などは座標のメンバ変数などを引数に渡して行うと思います
むしろ連鎖と言うより参照が多いかもしれません

そういう事になるとメンバ変数が非公開にできないですかね
色々なパターンで参照するわけですから・・

例えばボスのクラスのパターン関係のメンバ関数の中で
弾のクラスの登録関係のメンバ関数を呼び出して
引数に自機クラスの座標関係のメンバ変数を渡すとか・・

直接やっても間接的にやっても
これは依存するしかないと思うんですけどね・・

そもそもクラス同士で判定などを行うんだから
何らかの形で依存するのは当然なんでしょうかね・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 02:13
by Justy

>相互に依存しないで実現できるのでしょうか?
>自機とボスなどは相互に依存するしかないような気がするのですが・

 どういう状況で相互に互いの情報が必要があるのか、によって
変わってきますが。

 例えば、自機・ボス各々が互いをホーミングで攻撃するために座標が
必要だったとしましょう。

 まず、自機からはボスのクラスを直接参照するようなことはないですよね。
 それだと汎用性がないですから。

 ここはエネミー(の抽象クラス)リスト、或いはターゲットオブジェクト(攻撃対象となる
マーカークラス)リストを取得し、その情報を元に対象となるエネミー・ターゲットを選び出して
ホーミングクラスを生成します。

 で、ボスの方はボスという存在が特殊なので、普通に自機を直接見てしまっても
いいかもしれませんが、ボス戦を管理するクラスか何かが自機の位置を取得してボスに通知するとか、
自機のターゲットオブジェクトを取得して、ボスに設定しておけばそのターゲットオブジェクトから
対象の位置を取得できるので自機のことを直接知らなくて済みます。

 他にも、コールバックを利用して情報を通知するなど、あくまで一例ですが、
互いを直接参照しなくても情報を得る手段はいろいろあります。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 02:30
by Justy

> 直接やっても間接的にやっても
> これは依存するしかないと思うんですけどね・

 ゲームのように相互に情報をやりとりするケースが多いことはたしかですが、
だからといって、末端同士が直接アクセスしあうのはさすがにそうそうないでしょう。


 一番判りやすい例だとコリジョン判定でしょうか。
 
 コリジョン判定なんかは自機やボス、弾、アイテムなどの情報を直接参照していたら
オブジェクトの種類が増える度に大変になるので、当然別のオブジェクトを介して
判定することになるはずです。

 なんらかの抽象化……例えばコリジョンオブジェクトをつくり、形状や属性、ヒット時の
コールバックなどの情報を一元化して管理するようにして、それをコリジョン判定クラスに登録、
属性的に判定を行う必要があるもの同士を総当たり的に判定し、接触しているなら
双方にどういう状況で何に当たったかなどの情報をコールバックで通知すれば
通知先が自機とかエネミーとか関係なく処理できます。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 08:56
by たいちう
> 全部と言うわけではありませんがクラスは強い連鎖構造にあるので
> クラスは全体の半分ぐらいのクラスと関連しています

この設計がまずいのでしょう。
組木紙織さんの案などが参考になると思いますが、
どの点で無理だと思います?

あと、間違いを一応修正。
×:class Boss:public Boss
○:class Boss:public Kitai

> そういう事になるとメンバ変数が非公開にできないですかね

組木紙織さんの案の方向だと、継承させてprotectedにできます。

class Kitai {
  static int Hantei(Kitai & kitaiA, Kitai & kitaiB);
};

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 17:13
by チルチル
>例えば、自機・ボス各々が互いをホーミングで攻撃するために座標が
>必要だったとしましょう。
>まず、自機からはボスのクラスを直接参照するようなことはないですよね。
>それだと汎用性がないですから。

あれ?これって汎用性が無かったんですか・・
でも参照する状況に応じてオブジェクト郡を作成するのは荷が重いので
例えば自機からボスへの角度を返す関数などを作って呼び出すのはどうでしょうか?
これなら何かあっても関数の中だけ変更すれば良いから許容範囲ではないでしょうか?

直接参照はダメなんでしょうかね・・
できるだけ現実世界に近い関係がベストだと思っていたので
ボスを狙うならボスを見るのが普通な気がするんですが
プログラムではそうも言っていられないのでしょうかね・・

>コリジョン判定なんかは自機やボス、弾、アイテムなどの情報を直接参照していたら
>オブジェクトの種類が増える度に大変になるので、当然別のオブジェクトを介して
>判定することになるはずです。

う~んまあそうですが
オブジェクトにしたらオブジェクト関係を追加していかないといけないので
手間は同じような気もするんですけどね・・

これも荷が重いので座標などを引数に渡すと判定してくれる関数を作って各所で呼ぶのはどうでしょうか?
弾や敵のループ部分が増えるぐらいなら許容範囲にならないでしょうか・・

>この設計がまずいのでしょう。
>組木紙織さんの案などが参考になると思いますが、
>どの点で無理だと思います?

どこからを関連していると言うかは微妙ですが
とりあえずクラスの作成部分をコメントアウトしたらエラーが出る場合に関連していると解釈しています

しかしクラスなんだからどこかで呼び出されているはずなので
クラスは全て何かと関連している事になりますね・・

関連の強さの判断は難しいので中途半端に細工すると自爆しそうなんですよね・・

>組木紙織さんの案の方向だと、継承させてprotectedにできます

クラスの関連関係はどんどん増えていくので
継承はどこかで手詰まりになる可能性が高いので気が進みませんね・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 18:05
by ねこ
Justyさんも書いてるけど単純にクラス名.hとクラス名.cppで管理すればいいんじゃない?

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 19:27
by チルチル
cpp分割は初期化順番が確定していないとダメなので無理ですね・・

クラス名.hは一応やっていますね
選択画面は散らかっていますが・・

当初の目的はメンバ関数の定義を全部後ろにずらす事でほぼ解決しているのですが
クラスの関係性に問題があるようです・・

そういえばJustyさんのサンプルを見て思ったんですが
extern宣言ってプロトタイプ宣言の代わりにもなるんですね
オブジェクトとインスタンスを宣言すれば別解になるかもしれません・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 20:14
by バグ
『初期化順番が不定だと困る』という設計を見直してはいかがでしょうか?と、解答者の皆さんは言われているんだと思いますよ。

当たり判定などの相互参照が必要な場合は、『自機クラスと敵機クラスの両方のオブジェクトを使用しなければならないクラス』にメンバ関数として実装すればよいだけでしょう?

できるだけ、他クラスに依存するような設計は避けるべきです。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 20:28
by チルチル
>『初期化順番が不定だと困る』という設計を見直してはいかがでしょうか?

全てのコンストラクタで初期化関数を呼べば
順番に影響されることは無いと思いますが
無駄にコードの量が増えるのでダメですね・・

コンストラクタで読み込みを行うメリットが大きすぎるので
そこは残したままで見直す必要がありますね・・

>『自機クラスと敵機クラスの両方のオブジェクトを使用しなければならないクラス』にメンバ関数として実装

これは良くわからないですね
そんなクラスがあるのでしょうか?

メンバ関数に実装しても
間接的に依存しているように見えるのですが・・

>できるだけ、他クラスに依存するような設計は避けるべきです

そういえば一般的にどういう状態を「依存」と言うのでしょうか?

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 20:58
by ねこ
<cpp分割は初期化順番が確定していないとダメなので無理ですね・・
これの意味が分からないんだけど・・・cppにはメンバ関数の処理内容を記述するだけで初期化関係無いよ。

今回の問題はA・Bお互いのクラスでお互いを参照する際にコンパイルが通らない、俗に言う「循環参照」のエラーじゃないの?
Justyさんの例のように下にずらずら書くと分かりずらいから分ければって言ってるんだけど。


後、もうちょっと具体的なソースを提示してみたらどうかな?
多分問題自体はちゃんとしたソースを提示すれば回答者の誰もが簡単に答えられる程度だと思うんだけど
それをしないでコード量が増えるとか管理がしづらいとか表現されてもそれについての解決法があっても答えられない。
個人的にはクラス定義上定石だと思われるようなコードを無駄と感じてるんじゃないかと思います。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 21:28
by チルチル
>これの意味が分からないんだけど・・・

インスタンスが作成される順番がバラバラになるって事でしょうかね・・

>俗に言う「循環参照」のエラーじゃないの?

循環参照ではないですね
前のクラスから後ろのクラスが見えないって事でしょうか・・

>Justyさんの例のように下にずらずら書くと分かりずらいから分ければって言ってるんだけど

冷静に考えると現状でも分けてはいますね・・
何がわかりにくいかと言うと
メンバ関数だけ他に書くのがわかりにくいです
まあこれを言うと本末転倒ですが・・

>後、もうちょっと具体的なソースを提示してみたらどうかな?

そうですね、他の問題も見つかるかもしれませんし・・
ですが問題になっている部分はまだ作っていないので
推測してもらうしかないですね・・

作ってから「こんなハズじゃなかった」って事にならないように質問させていただいたので・・

>多分問題自体はちゃんとしたソースを提示すれば回答者の誰もが簡単に答えられる程度だと思うんだけど

あれ?そうだったんですか?
1人で未知の分野に悩んでいたんですが・・
当初の「見えるようにする方法」は簡単かもしれませんが
新たに出てきた「連鎖を回避する良い方法」もパッと解決できるのでしょうか・・

>個人的にはクラス定義上定石だと思われるようなコードを無駄と感じてるんじゃないかと思います。

まさにそうです
定石ではあるけど個人的にしっくりこないので敬遠しています・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 22:42
by ねこ
>インスタンスが作成される順番がバラバラになるって事でしょうかね・・
>循環参照ではないですね
>前のクラスから後ろのクラスが見えないって事でしょうか・・
それボスから自機、自機からボスって仕組みにしたら相互参照になりますよ。
どういうイメージしてるんでしょう?ヘッダとcppに分けた場合の例を以下に記載します。
(A.h)
class B;
class A {
protected:
	B* m_pB;
public:
	// コンストラクタ
	A();
};
(A.cpp)
#include "A.h"
// コンストラクタ
A::A()
{
	m_pB = NULL;
}

(B.h)
class A;
class B {
protected:
	A* m_pA;
public:
	// コンストラクタ
	B();
};
(B.cpp)
#include "B.h"
// コンストラクタ
B::B()
{
	m_pA = NULL;
}

(main.cpp)
#include "A.h"
#include "B.h"
A g_A;
B g_B;
このように記述するとAからB、BからAを参照できる仕組みになっているのが分かるでしょうか?
チルチルさんのソースのようにヘッダだけに記述するとこのように相互参照出来ません。
自機からボスが見れ、ボスから自機が見れるようし、かつヘッダ定義の下に書きたくないならこうするしか手はありません。
なので、ヘッダとcppを分けましょうと言っています。ヘッダとcppが分かれる、というよりはヘッダはcppのための定義用おまけ、程度の認識にしたらどうでしょうか?
後、基本的にクラスヘッダはソリューションの一覧には出しません。cppだけ見れば分かるようにしておくのが理想だと思ってます。

<あれ?そうだったんですか?
<1人で未知の分野に悩んでいたんですが・・
<当初の「見えるようにする方法」は簡単かもしれませんが
<新たに出てきた「連鎖を回避する良い方法」もパッと解決できるのでしょうか・
こんなのはただの考え方の問題です。
クラス構造は基本的に樹木のような構成になります。
自機・ボスを枝先とするなら、枝の根本が必ずあります。根本では双方を参照できるため、相互的な処理はそこでしましょう、というのが各人からの意見です。
チルチルさんは根本を見ないで、枝先から枝先へ糸をつなげるような仕組みを作ろうとしてますよね?
だからオブジェクトが増えるたびに連鎖して参照が増えて・・・となってしまうわけです。
ちゃんとどこで処理するかを考えて構成すれば、クラス単位の無駄な参照は激減します。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 23:12
by チルチル
ヘッダの上で相手のクラス名が書かれている意味が良くわからないですね、extern宣言の代わりでしょうか?

メンバ関数はどこかで繋がっていればある程度見えなさそうな感じでも
見えるというのはなんとなくわかりますが・・

全体的に何をやっているかはピンと来ないですが
cppが違えばクラスの本体が複数あってもOKと言うのはわかりました
クラスのcppでメンバ関数と連結しておけばmain.cppで有効になるのは理解に手間取りそうです

これなら初期化順番は固定なので実装できない事は無いですね
理解できるかは怪しいですが・・

クラスが増えるたびに宣言とヘッダのインクルードが増えてしまいますが
これもヘッダにまとめてしまえば何とかなりそうですね
ヘッダの中でヘッダをインクルードするのは確かタブーですが
同じ事を何度も書く方がタブーでしょうかね・・

クラス構造は樹木なんですか・・
私はクラスに限らずオブジェクトはクモの巣のような構造で考えていたんですけどね・・

自機やボスが枝先としたら根本は何でしょうか?
双方を参照できると言ってもメンバを公開しておけば普通に参照できますからね・・
この場合は双方を参照するのが自然な立場って事でしょうか?
自機とボスって独立しているような気がしますが
これの根本って言うと・・世界クラスとでも言えば良いのでしょうか?

根本で双方を関連させれば自機とボスは依存していないように見えますが
その代わりに根本が自機とボスに依存する形になるような気がするのですが・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月06日(火) 23:25
by バグ
考え方が逆ですね。
根元へ行けば行くほどにオブジェクトはそれ1つで完結する独立したものになります。

そうしないと、機能をカプセル化する意味が全くありません。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月07日(水) 00:40
by ねこ
<ヘッダの上で相手のクラス名が書かれている意味が良くわからないですね、extern宣言の代わりでしょうか?
こうしておかないとクラス定義時にAってのが何か認識されません。
Aの定義が後にある場合に仮に定義しておくことで「A* m_pA」が定義出来ます。

<根本で双方を関連させれば自機とボスは依存していないように見えますが
<その代わりに根本が自機とボスに依存する形になるような気がするのですが・・
根本は自機とボスの変数をどこに定義しているかによりますが、どちらもグローバル変数ならグローバル領域が根本、あるクラスのメンバー変数であればそのクラスが根本になります。
依存という単語を「相互依存」と考えていませんか?
配列で言えば配列変数が根本、値が枝先になります。根本からは枝先の値が見えますが、枝先からは配列の情報は見えません。こういう形の構成だってありえるわけです。


とりあえずここらで簡単に構成作ってみませんか?
全く手もつけてない状態で分からない分からないと言われてもこちらも限界があります。
最初から完璧な構成なんてできっこないので、作りつつ自分の理想に近づくように修正してみてください。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月07日(水) 01:00
by チルチル
>根元へ行けば行くほどにオブジェクトはそれ1つで完結する独立したものになります

そうなんですか?
根本の独立性が高いって事は枝先を参照できないって事になる気がするんですが・・

根本に行くほど独立していると言う事は
枝先は独立性が低くなるって事になる気がするんですが
それだと枝先同士で参照する方が良いって事になってしまいます・・

バグさんの回答を入れると良くわからなくなってしまったのですが
とりあえずねこさんの回答から解釈してみると

枝先クラスは根本クラスだけを連鎖すれば良いって事でしょうか?

そして根本クラスは枝先クラスを参照すれば良いのでしょうか?

>とりあえずここらで簡単に構成作ってみませんか?

例えば

枝先クラスとして自機クラスとボスクラスがあり
根本クラスとして世界クラスがあります
自機クラスとボスクラスは世界クラスを見ています
世界クラスは自機クラスとボスクラスを見ています
自機クラスとボスクラスはお互いを見ていません

自機クラスがボスへの角度を知りたくなった場合は
世界クラスのメンバである「自機からボスへの角度を返す関数」を呼び出す
世界クラスのメンバ関数は自機クラスとボスクラスを参照して角度を求めて戻り値として返す
自機クラスが角度を受け取る

根本で相互的な処理をするってこういう事でしょうか?

他のクラスの事が知りたくなったら自分で見に行くんじゃなくて
根本クラスに聞いてみれば良いって言う事になり
枝先クラスは他のクラスに直接的に依存する事は無くなるので
枝先クラスの中では根本クラス以外の連鎖も参照も必要無くなって
根本クラスの中でだけ連鎖と参照が増えて行くという事になる

しかし根本クラスは単体では完結してくれないですね
関連の種類だけメンバ関数が必要になりますし・・

>依存という単語を「相互依存」と考えていませんか?

自分のクラス内で相手のクラスのメンバを呼び出したら自分は相手に依存していると解釈していますが・・
配列変数は値を呼び出せるので値に依存していると言う事になってしまいますが
さすがにこれは少し違う気がしますね・・

相手のクラスを消したら自分のクラス内で「未定義のシンボル・・・」がでる状態を
依存していると呼ぶのは間違っているのでしょうか・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月07日(水) 01:20
by チルチル
でも良く考えたら

枝先クラスは根本クラスのメンバ関数を連鎖して

根本クラスは枝先クラスのメンバ変数を参照するので

これは相互依存になるんでしょうか?

Re:クラスのプロトタイプ宣言

Posted: 2009年10月07日(水) 01:22
by ねこ
構成ってのは実際に作ってみてコンパイルして関数を作ってみて実行してみて、って意味合いね。
実際に作れば何が良くてどういう問題が起こりえて定義方法に不都合が生じるのかが掴めると思いますので。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月07日(水) 12:45
by たいちう
ねこさんに同意。

今までの流れで判断すると、チルチルさんはクラス設計というものを理解していません。
だからこそ、ここで質問しているのであって、もちろん構わないのですが、
「独立」とか「連鎖」とか「依存」とかいう言葉についても、
多くの回答者とは違う使い方・理解をしているようです。

チルチルさんは十分先の見通しが立ってから着手したいようですが、
今は試行錯誤で理解を深めるのが良いと思います。
ソースコードを伴った具体的な質問の方が説明もしやすいし、
理解もしやすいでしょう。


とりあえず「自機」と「ボス」だけ(必要なら「世界」等も)を定義して、
「自機」から見た「ボス」の角度を求める関数を作ってみてはどうでしょうか。
その上でなら、「雑魚」や「雑魚2」を追加する時どうなるのか、
具体的に議論できると思いますが。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月07日(水) 18:20
by チルチル
>クラス設計というものを理解していません

何か一般的な定石があるのでしょうか?

>「独立」とか「連鎖」とか「依存」とかいう言葉についても、
>多くの回答者とは違う使い方・理解をしているようです。

一般的にはどういう意味なんでしょうか?

>とりあえず「自機」と「ボス」だけ(必要なら「世界」等も)を定義して、
>「自機」から見た「ボス」の角度を求める関数を作ってみてはどうでしょうか

作ってみました、関数と言うかメンバ関数ですが・・
自機やボスの知らない所で世界がメンバを操作すると言う構造になりました

世界が消えたら正常には動きませんが
自機とボスで「未定義のシンボル・・・」は出なくなりましたね

でも自機とボスが消えたら世界で「未定義のシンボル・・・」とかが出ますから
こういうのを依存と言うのでしょうかね・・

枝先が根本に聞くんじゃなくて
根本の方で一方的に回答を押し付ける感じになっています
でも枝先はその為のポストがどんどん必要になりますね
こうなると移動パターンなどは世界の方でやった方が良くなってしまいます・・

根本に行くほどクラスは単体で完結するらしいですが
相互的な処理を根本でやったら単体で完結してくれないんですが・・

>その上でなら、「雑魚」や「雑魚2」を追加する時どうなるのか、

今回の場合だと世界クラスの中身が増えて行きますかね・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月07日(水) 18:43
by MNS
friendはダメですよ。せっかくのカプセル化のメリットが失われてしまいます。

自機から世界が見えていたほうがいいのか、などという問題は、
時と場合によっても、個人の好みによっても変わってくるので、
それらに難癖は付けられませんが、
"包含"というものを使うことをオススメします。

私はそういう依存やら根本やらなどの単語は良くしらないのですが、
今の状況だと自機もボスも世界もみんな、いわゆる枝先になってはいませんか?
現実に置き換えると、世界というものが自機やボスを包含する、
つまり世界が自機やボスのインスタンスを持つ、という構造になりますが、
そうすると、世界が根本、自機やボスが枝先、となると思います。
また、こういう構造であれば自機がボスに直接アクセスすることは出来なくなり、
いっそうクラスの関係がシンプルなものになるとは思いませんか?

Re:クラスのプロトタイプ宣言

Posted: 2009年10月07日(水) 19:38
by チルチル
>friendはダメですよ。せっかくのカプセル化のメリットが失われてしまいます。

あ~やっぱり無理がありましたか・・
これは見直しが必要ですね

>自機から世界が見えていたほうがいいのか、などという問題は、
>時と場合によっても、個人の好みによっても変わってくるので、

枝先と根本が相互に見えていても良いんでしょうか?
それなら根本に聞いてみるという実装の仕方でも良いかもしれませんが
枝先の中に根本が連鎖する事になってしまいますね
まあ根本が消えたらどっちみち正常に動かないんだから別に関係ない気もしますが・・

>"包含"というものを使うことをオススメします。

やっぱり包含の方が良いんでしょうかね・・
根本クラスのコンストラクタで前処理ができなくなりますが
専用のクラスを追加するぐらいなら問題無さそうですね
インスタンス順番は固定のようですし・・

>今の状況だと自機もボスも世界もみんな、いわゆる枝先になってはいませんか?

なっていますね・・
直感的であるというのは重要のようです・・

>現実に置き換えると、世界というものが自機やボスを包含する、

やはり一番直感的なのは包含でしょうかね・・
枝先が根本クラスのメンバを呼び出す時にインスタンス名が不要になる気もしますし・・

>また、こういう構造であれば自機がボスに直接アクセスすることは出来なくなり、
>いっそうクラスの関係がシンプルなものになるとは思いませんか?

クラスの関係に現実的に見えないという強制力が付くので良いですね
しかし包含同士はお互いが見えないんですね
この辺は良く知らないのでどこからどこが見えるのかを勉強しないと・・

同じ世界に住んでいるのにお互いが直接見えないのも変な気がしますが
現実でも光を介して見ているんだから納得できない事も無いかな・・

包含の場合は枝先から根本が見える事が確定していますから悩む必要は無さそうですね
枝先から根本への連鎖を作るかは迷いますが・・

でも良く考えたらコンストラクタを包含するメンバクラスより上に書けば前処理も可能ですね・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月07日(水) 22:59
by 組木紙織
たいちうさんありがとございますサンプルコードを直していただいて。
全然気づきませんでした。




少しだけ添付されたコードを眺めただけなのですが、違和感満載な感じがしました。
たぶん以下のリンク先が参考になるかと。

C++で読むデザインパターン(Design Patterns read by C++)
ttp://www.01-tec.com/document/cpp_design_pattern.html

Head Firstデザインパターン―頭とからだで覚えるデザインパターンの基本 (単行本)
http://www.amazon.co.jp/Head-First%E3%8 ... 049&sr=8-7

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 08:56
by たいちう
> 何か一般的な定石があるのでしょうか?

将棋で駒の動かし方を知っているだけの人よりも、
定石を覚えた人の方がずっと強いでしょう。
プログラミングでは駒の動かし方が文法、
デザインパターンなどが定石の一つと言えるでしょう。
但し将棋と同様に正解はありません。
機械的に定石を覚えるよりも、何故この一手が良いとされるのかを
理解するように努力しましょう。


> 同じ世界に住んでいるのにお互いが直接見えないのも変な気がしますが
> 現実でも光を介して見ているんだから納得できない事も無いかな・・

現実世界の全てをモデル化して計算することは不可能です。
必要最低限の部分のみを再現しましょぅ


継承を使って書きなおしてみました。
コンパイルを通るようにしてみてください。
class Kitai {
protected:
	float x;
	float y;
public:
	float getAngle(const Kitai *target) {
		return atan2(target->y - y, target->x - x);
	}
	virtual void Run() = 0;
	virtual void SetAngle() = 0;
};

class Player : public Kitai {
	float bossAngle;
public:
	void Run() {
		//
	}
	void SetAngle() {
		bossAngle = getAngle(world.getBoss());
	}

	// その他必要なもの
};

class Boss : public Kitai {
	// その他必要なもの
};

class World {
	Kitai* player;
	Kitai* boss;

public:
	World() {
		player = new Player();
		boss = new Boss();
	}

	Kitai* GetBoss() {
		return boss;
	}

	void Run() {
		Player.SetAngle();
		Player.Run();
		Boss.Run();
	}
};
手を抜いてクラス宣言に実装を書いていますので、hとcppとに分割してください。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 09:57
by チルチル
ポインタを使った包含の方が良いんでしょうかね?

継承を使うのはピンと来ませんがテンプレートの代わりにはなるかもしれませんね

抽象クラスについては勉強が必要のようです・・

ボスのメンバの値が直接知りたい場合は
GetBossX(),GetBossY(),GetBossAngle()とかをいちいち作るんでしょうか?
ポインタを返してもらってGetBoss()->X;とかの方が簡単ですが
ボスが見えないけど意識していると言う事になってしまいますね
まあ、そういうメンバはすでにあるんだから関係ない気もしますが・・

そういう意味だとPlayer.SetAngleは汎用的ではないから必要ないかもしれませんね
直接ボスを参照しなければ世界を通してボスを参照しても大丈夫かも・・

自機とかボスとかは良いとして弾とかはどうしましょうか?
弾クラス内で線形リストを作るのと世界クラス内で弾クラス線形リストを作るのが考えられますが・・

どちらかというと前者の方が良さそうですね、たいちうさんのサンプルを見る限りは
1つの相互処理を行う為に各クラスごとにいちいち関数を作るのは一般的のようですし

ボスクラス内で「弾を登録するメンバ関数」を呼び出して
その中で世界クラスの「弾を登録するメンバ関数」を呼び出して
その中で弾クラスの「弾を登録するメンバ関数」を呼び出す

前者だと弾から弾を登録する場合に世界を通す必要が無いですからね
まあ継承が使えなくなってしまいますが・・

そのままサンプルを使ったら勉強にならないので見ながら作ってみます

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 10:11
by conio
継承を使うのならば、基底クラスのポインタの配列にした方が良いのではないのでしょうか。
もしくはlistを使うとか。
------------------------
list<Kitai*> CharaList;  
------------------------

そうしないと、オブジェクトが増えるごとにゴチャゴチャしてしまいます。
---------------------------------------------
class World {
         Kitai* player;
         Kitai* boss;
         Kitei* item;
         Kitei* Zako;
         Kitei* Bullet;

public:
	World() {
                  player = new Player();
                  boss = new Boss();
                  item = new Item();
                  Zako = new Zako();
                  Bullet = new Bullet();
	}

	Kitai* GetBoss() {
		return boss;
	}

	void Run() {
                  Player.SetAngle();
                  Player.Run();
                  Boss.Run();
                  iten.Run();
                  Zako.Run();
                  Bullet.Run();
	}
};
---------------------------------------------
あと、上記のコードではデストラクタが無いのでメモリリークしてしまいます。

>>チルチルさん
参考になればと。
http://www.tnksoft.com/reading/classgame/

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 10:36
by チルチル
>list<Kitai*> CharaList;

これって標準ライブラリの一種でしたっけ?
線形リスト系は手動でやりたいです・・

>基底クラスのポインタの配列

手動でやるとどんな感じでしょうか?

>Kitai* player;
>player = new Player();

継承については良く知りませんが
KitaiクラスのポインタだとPlayerクラスで追加したメンバにアクセスできない気がするんですが・・

>あと、上記のコードではデストラクタが無いのでメモリリークしてしまいます。

あ~それ私も思いました・・

>そうしないと、オブジェクトが増えるごとにゴチャゴチャしてしまいます。

やっぱりポインタで包含した方が良いんでしょうか?
本体もゴチャゴチャしそうですが
相互処理の関数の方が多いのでゴチャゴチャしそうですね・・
その辺を短くまとめる良い方法とかないでしょうか?

>参考になればと。

さっそく見てみます

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 11:15
by conio
>>>list<Kitai*> CharaList;
>>これって標準ライブラリの一種でしたっけ?
>>線形リスト系は手動でやりたいです・・
自分で実装しても勿論問題ありません。

>>>基底クラスのポインタの配列
>>手動でやるとどんな感じでしょうか?
---------------------------
Kitei* Object[20];
int Max = 0;
Object[Max++] = new Player;
Object[Max++] = new Boss;
Object[Max++] = new Zako;

for(int i = 0; i < Max; i++)
Object.Run();
---------------------------
こんな感じです。
まぁ、この場合は動的確保などもしていないので、オブジェクトの削除や追加が自由に出来ません。
単純に、実行したいオブジェクトが固定だと決めている場合の時のみに限ります。


>>>Kitai* player;
>>>player = new Player();

>>継承については良く知りませんが
>>KitaiクラスのポインタだとPlayerクラスで追加したメンバにアクセスできない気がするんですが・・
KiteiクラスがPlayerクラスのメンバを直接操作するのではありません。
それぞれのオブジェクトのpublicな関数(上記だとRun関数)を呼び出し、
その関数内で、メンバなどの操作をします。
あくまでも、メンバを扱うのはそのメンバを持っているクラスです。
以前のコードでも、直接メンバを操作している部分は無いはずです。


>>>そうしないと、オブジェクトが増えるごとにゴチャゴチャしてしまいます。
>>やっぱりポインタで包含した方が良いんでしょうか?
>>本体もゴチャゴチャしそうですが
>>相互処理の関数の方が多いのでゴチャゴチャしそうですね・・
>>その辺を短くまとめる良い方法とかないでしょうか?

全てのオブジェクトを管理するクラスを作っておき、そのクラスに対して
---------------------------------------------------------------------------------------
・"Boss"というオブジェクトは登録されてますか?登録されていたら教えてください
・"Enemy"というオブジェクトは登録されていますか?登録されている全ての"Enemy"オブジェクトを
 順次教えてください
---------------------------------------------------------------------------------------
このように、命令を出してやればいいと思います。
直接オブジェクトを参照するのではなく、一元管理しているクラスがオブジェクトを探し出し、
オブジェクトがあれば そのオブジェクトの情報を取得して処理をする、という方法です。

先ほど提示したサイトに詳しく載っているので参考にされると良いと思います。
(ただし理解するのには、骨が折れるかもしれません)

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 11:39
by チルチル
>自分で実装しても勿論問題ありません。

クラスの線形リストってどうやって作るのでしょうか?

>まぁ、この場合は動的確保などもしていないので、オブジェクトの削除や追加が自由に出来ません。

ん?実行中に新たにインスタンスを作成する機会ってあるのでしょうか?

>それぞれのオブジェクトのpublicな関数(上記だとRun関数)を呼び出し、

ああ確かにRunは継承元で宣言されていますね
しかしメンバなどの操作とはメンバの値を調べる事も含まれるのでしょうか?
座標が知りたいのに角度その他まで返されると困ってしまうのですが・・

>全てのオブジェクトを管理するクラスを作っておき

え~と・・この場合は世界ですよね?

>"Boss"というオブジェクトは登録されてますか?登録されていたら教えてください

これは
「"Boss"というクラスがあるのは知っていますが、インスタンスされているか教えてください」って事でしょうか?

それとも
「"Boss"というクラスは存在するのでしょうか?存在するとしたらインスタンスされていますか?」って事でしょうか?

>そのオブジェクトの情報を取得して処理をする

情報はどんな形で渡されるのでしょうか?
ポインタだとしたら、とりあえずRun関数はあると思うから呼ぼうって事になるのでしょうか?

>あくまでも、メンバを扱うのはそのメンバを持っているクラスです。

ではやはりGetBossX(),GetBossY(),GetBossAngle()とかをいちいち作るんでしょうか?

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 12:04
by MNS
> クラスの線形リストってどうやって作るのでしょうか?
線形リストが持つ値をクラスにすればいいだけでしょうが、
そもそも線形リストが持つ機能といいますか、メリットデメリットをご存知でないのなら、
std::listを一度触れてみても良いと思います。

> ん?実行中に新たにインスタンスを作成する機会ってあるのでしょうか?
もちろんあります。例えば、弾のインスタンスは、弾を発射する時に作成すればよいです。
むしろ、それを行わないのなら、線形リストを使う必要はありません。

> しかしメンバなどの操作とはメンバの値を調べる事も含まれるのでしょうか?
基底クラスで共通したメンバを持つのならば、(座標、角度など)
そのメンバの値を返す関数などを基底クラスで定義すればよいです。

> これは
> 「"Boss"というクラスがあるのは知っていますが、インスタンスされているか教えてください」って事でしょうか?
> それとも
> 「"Boss"というクラスは存在するのでしょうか?存在するとしたらインスタンスされていますか?」って事でしょうか?
おそらく前者でしょう。

> 情報はどんな形で渡されるのでしょうか?
> ポインタだとしたら、とりあえずRun関数はあると思うから呼ぼうって事になるのでしょうか?
ポインタでも良いですし、参照でも良いでしょう。
いずれにせよ、呼び出せるメンバはpublicなもののみです。
例えば、敵が自機に向かって弾を撃つ処理を書くとき、
敵はいわゆる世界クラスに自機の情報を問い合わせ(これでポインタが返ってくるとして)
GetPosなど、座標を返すパブリックな関数を作っておき、
その関数から自機の座標を取得し、弾を撃ちます。

>ではやはりGetBossX(),GetBossY(),GetBossAngle()とかをいちいち作るんでしょうか?
ボスが何か基底クラス(座標、角度などが定義されているもの)を継承しており、
基底クラスでGetPosやGetAngleなどの関数が定義されていれば、
いちいちボスに対しそのような関数を作る手間は省けると思います。
もちろん、ボスで新たに定義されたメンバに対しては、それに応じた新たなアクセサを作る必要がありますが。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 12:33
by たいちう
> ポインタを使った包含の方が良いんでしょうかね?

クラスWorldは、その名前からして全てのインスタンスを
所有したいものと思いました。
一方、Worldが全てのクラスの詳細を意識しないといけないと、
Worldのソースコードが膨れ上がります。
WorldはPlayerやBossの詳細は気にせず、複数のKitaiを扱います。
World.hで、Player.hやBoss.hをインクルードしないで済むように、
ポインタでPlayerやBossのインスタンスを管理する方法を書きました。

今までのやり取りからは、私にはこれが一番自然と感じられますが、
他の方法も可能です。どんな方法でも良いというわけではありませんが。


> 継承を使うのはピンと来ませんがテンプレートの
> 代わりにはなるかもしれませんね
>
> 抽象クラスについては勉強が必要のようです・・

まだ駒の動かし方を知らないということです。
龍や馬のように強力ですので、是非身につけてください。


> ボスのメンバの値が直接知りたい場合は
> GetBossX(),GetBossY(),GetBossAngle()とかをいちいち作るんでしょうか?
> ポインタを返してもらってGetBoss()->X;とかの方が簡単ですが
> ボスが見えないけど意識していると言う事になってしまいますね
> まあ、そういうメンバはすでにあるんだから関係ない気もしますが・・
>
> そういう意味だとPlayer.SetAngleは汎用的ではないから必要ないかもしれませんね
> 直接ボスを参照しなければ世界を通してボスを参照しても大丈夫かも・・

質問が散漫で何を聞きたいのか分かりません。
投稿前に読み直して、自分の考えを正しく伝える努力をもっとして下さい。

1つ言えるのは、ボスのメンバの値を誰が知りたいのか、という問題です。
カプセル化を意識して、この情報はどのクラスが持つか、公開するべきか、
この情報で計算するのはどのクラスであるべきか、を考えます。

もしも、World::GetBossX()のような関数を作り、Player::Run()から
呼び出すことを考えていたのならば、これは多分間違いでしょう。
せめてKitai::GetX()にしましょう。
Player::Run()の中で、Boss::xが何故必要なのかを具体的に書いてくれれば、
より良い設計を提示できると思います。


> そのままサンプルを使ったら勉強にならないので見ながら作ってみます

そのままだとコンパイルも通らないし、conioさんご指摘の問題もあるし、
十分勉強になると思いますけどね。まぁご随意に。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 12:58
by チルチル
>線形リストが持つ値をクラスにすればいいだけでしょうが、

あ~やっぱりそうですか・・
値はKitei*でしょうかね?
多分これだと派生クラスで追加したメンバは見えないですから
派生クラスで追加するメンバは全部privateで良いんでしょうか?

>そもそも線形リストが持つ機能といいますか、メリットデメリットをご存知でないのなら、

何となくはわかっていると思うんですけどね・・
個人的には「Boss.」とかが「P->」になって式が短くなるのが一番のメリットでした・・

>もちろんあります。例えば、弾のインスタンスは、弾を発射する時に作成すればよいです。
>むしろ、それを行わないのなら、線形リストを使う必要はありません。

やはり
>弾クラス内で線形リストを作るのと世界クラス内で弾クラス線形リストを作るのが考えられますが・・
これで後者を選んだ場合は必要ですね

前者の場合は必要ないですが、弾が他の弾を参照できるのはマズイでしょうか?

>そのメンバの値を返す関数などを基底クラスで定義すればよいです

と言う事は派生クラスで追加した変数や関数はどうやって利用するのでしょうか?

>GetPosなど、座標を返すパブリックな関数を作っておき、

では自機の座標が知りたい場合は
float X=GetPlayer()->GetX();
見たいな感じになるのでしょうか?

>もちろん、ボスで新たに定義されたメンバに対しては、それに応じた新たなアクセサを作る必要がありますが。

アクセサってメンバ関数とかでしょうか?
Kitei*では追加した関数が見えないと思うのですが・・

>World.hで、Player.hやBoss.hをインクルードしないで済むように、

ああなるほど共通しているKiteiだけ見るならKiteiの使い方だけ知っていれば良いと言う事ですか

>1つ言えるのは、ボスのメンバの値を誰が知りたいのか、という問題です。

自機がボスの座標を知りたい場合に
GetBossX();とするか
GetBoss()->X;とするかでしょうかね・・

でも確かに
GetBoss()->GetX();とか
GetX(GetBoss());の方が良さそうですね・・

>そのままだとコンパイルも通らないし

そういえばそうでしたね・・
やはり見ながら通るように作らないと・・

しかし世界クラスがポインタを共通しているKitei*で持っていると
派生クラスで追加したメンバ関数とかにアクセスできないですね・・
そういうのは共通しているRunとかの中で内部だけで解決できるようにするのでしょうか?

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 13:03
by conio
>>>自分で実装しても勿論問題ありません。 
>>クラスの線形リストってどうやって作るのでしょうか?  
ノードの中のデータをintにすればint型の線形リストになりますし、
ノードの中のデータをfloatにすればfloat型の線形リストになります。

同じように、ノードの中のデータを基底クラスのポインタにすれば、クラスの線形リストになります。
それだけのことです。 難しく考える必要はありません。

>>>まぁ、この場合は動的確保などもしていないので、オブジェクトの削除や追加が自由に出来ません。 
>>ん?実行中に新たにインスタンスを作成する機会ってあるのでしょうか? 
インスタンスを作成したいのであれば、
インスタンスを作成する関数を作って、その関数を呼べばよいのではないのでしょうか。
作成する機会を作りたいのなら、自分で作成する機会を作ればいいのです。
---------------------------------------------------------
	void AddObject(Kitai* m){
		if(Max < 20){
			Object[Max++] = m;
		}
	}
---------------------------------------------------------
既に述べたように、単純に追加する処理しかしていないので、オブジェクトが20を超えると
追加が出来なくなりますし、削除する機能もありません。
「自由自在に削除や追加が出来ない」と言ったのはそういう意味です。

ともかく、削除や追加を自由に出来る様にしたいのならば、配列ではなくリスト構造にすればいいかと。

>>ああ確かにRunは継承元で宣言されていますね 
>>しかしメンバなどの操作とはメンバの値を調べる事も含まれるのでしょうか? 
>>座標が知りたいのに角度その他まで返されると困ってしまうのですが・・ 
オブジェクトを戻り値として受け取って、それからメンバアクセス演算子などでアクセスすればいいと思います。
-------------------------------------------------
・角度を受け取る関数
・位置を受け取る関数
・●●を受け取る関数
     :
     :
-------------------------------------------------
と言う風にしたら、メンバを追加した文だけそのメンバを取得する関数が増えることになりますし、
明らかに無駄です。

ポインタで受け取って、アロー演算子で特定のメンバを調べるなりした方がいいと思います。

-----------------------------------------
Enemy* e;
e = Enemy型のポインタを返す関数();

e->PtX
e->PtY
など。
-----------------------------------------

>>これは 
>>「"Boss"というクラスがあるのは知っていますが、インスタンスされているか教えてください」って事でしょうか? 
>>それとも 
>>「"Boss"というクラスは存在するのでしょうか?存在するとしたらインスタンスされていますか?」って事でしょうか?
先ほどのコードだと基底クラス型のポインタの配列 Object[20]がありましたよね?
その配列の中に、派生クラスBossが入れられている場所を探し、見つかればそのポインタを返す、
と言う処理だと思って下さい。

>>>あくまでも、メンバを扱うのはそのメンバを持っているクラスです。 
>>ではやはりGetBossX(),GetBossY(),GetBossAngle()とかをいちいち作るんでしょうか?
違います。
既に述べたように、メンバごとに関数を作るようにすると、メンバの数だけ関数が増えます。
オブジェクトのポインタを受け取って、そのポインタを通じてメンバを調べるなりした方が良いと思います。

ついでに、当たり判定などは
Playerクラス、Enemyクラスなどの内部で行うのではなく、
それらを一元管理するクラスの方で行った方が良いでしょう。

http://www.tnksoft.com/reading/classgam ... 00/005.php
当たり判定についてはここら辺で解説しているので、コレを参考にして下さい。
こちらでは書きません。
(長くなりますし、2度手間になってしまいます。)

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 13:36
by チルチル
>ポインタで受け取って、アロー演算子で特定のメンバを調べるなりした方がいいと思います。

なるほど、言われてみればその通りですね
継承のおかげで安全性も増している事だし・・

>Enemy型のポインタを返す関数();

これだとボスのクラスに自機やザコのクラスの宣言が見える必要がある気がするんですが・・

>その配列の中に、派生クラスBossが入れられている場所を探し、見つかればそのポインタを返す、

どうやって探すのでしょうか?
世界クラスは基底クラスの構造しか知らないようですが・・

>それらを一元管理するクラスの方で行った方が良いでしょう。

サイトは良くわかりませんでしたが
世界クラスの中で自機とボスのポインタから座標にアクセスして
当たっているか判断すれば良いって事でしょうか?
自機からすると「世界から何かに接触したと言う連絡があったので残機を減らそう」って事でしょうか?
メンバの変更はクラス内でやるべきだから世界は当たっているかだけ連絡して
残機を減らす処理は自機クラス内にそういうメンバ関数を定義するべきでしょうか?

>ともかく、削除や追加を自由に出来る様にしたいのならば、配列ではなくリスト構造にすればいいかと。

う~ん弾が他の弾を自由に参照できても良いかによりますね・・

>オブジェクトを戻り値として受け取って、それからメンバアクセス演算子などでアクセスすればいいと思います

派生クラスで追加した変数とか関数にアクセスしたい時はどうすれば良いのでしょうか?

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 15:26
by skyblue
ヘッダーファイルの自動生成ならたぶんこれ↓
http://www.vector.co.jp/soft/win95/prog/se324412.html

関係ないかもしれませんが。
シューティングで簡単なものだったら、
配列と関数を使って、それぞれの処理ごとに関数を作ればいい。(座標を使ってあたり判定を行う)
そうすればボス(自機)が動いたときに自機(ボス)を動かす処理を作ればいいのと思います。
main関数で呼び出しだけ。そうすれば、ファイルを分けなくても、cpadなら関数にジャンプできますし。
変数を配列の変わりに、ポインタを使う手もありますが。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 15:39
by チルチル
う~ん環境うんぬんは現状で良いでしょうかね・・

>配列と関数を使って、それぞれの処理ごとに関数を作ればいい。(座標を使ってあたり判定を行う)
>そうすればボス(自機)が動いたときに自機(ボス)を動かす処理を作ればいいのと思います。

すいません・・
これの意味がさっぱりわからないです
どういう事か詳しく説明してください・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 16:07
by conio
>>>その配列の中に、派生クラスBossが入れられている場所を探し、見つかればそのポインタを返す、 
>>どうやって探すのでしょうか? 
>>世界クラスは基底クラスの構造しか知らないようですが・・
typeid演算子を使えば、基底クラス型のポインタにどのオブジェクトが入っているかを
調べる事ができますよ。
あと、名前と基底クラス型のポインタを構造体にしてしまっておいて、名前で検索するという事も出来ます。
----------------------
typedef struct{
   string name;   //名前
   Kitei* Object;   //基底クラス型のポインタ
}ObjectList;

ObjectList Object[20];
----------------------
みたいな。

>>サイトは良くわかりませんでしたが 
>>世界クラスの中で自機とボスのポインタから座標にアクセスして 
>>当たっているか判断すれば良いって事でしょうか?
「よく分からなかった。」で諦めるのではなく理解できるまで頑張ってみてはどうでしょうか。
よく分からない→質問する→やっぱりよく分からない→質問する→それでもよく分からない→質問する
の繰り返しになって、半永久的に理解が進まない可能性があります。

よくあるのが、下記のようなパターンです。
回答は貰ったけど理解出来ないから諦める → 他の場所で質問しなおす
いわゆる、マルチポストという奴ですね。

ともかく、質問の前に考えることも大事です。
あと、ゲームには
Playerや、Enemy、Bossのようにキャラクターの役割をするものや、
Texture、Sound、Inputのようにキャラクターではないものも存在します。

なので、"世界"クラスに処理を持たせる、ではなく"世界"クラスから、
"キャラクター管理"クラスを派生させ、そのキャラクター管理クラスの中で処理をする、
という風にしたほうが良いでしょう。


>>>Enemy型のポインタを返す関数(); 
>>これだとボスのクラスに自機やザコのクラスの宣言が見える必要がある気がするんですが・・
そうですね。
先ほどのサイトのソースを見ても分かるように、CEnemyBase型のポインタを使いたい場合は
ヘッダをインクルードします。
------------------------------------------------
#include "EnemyBase.h" 
void CPlayerBullet::Exec()   
{   
    /* 弾の移動処理 - 省略 */  
  
    // 優先度から敵クラスを列挙する   
    CPrioEnum pe;   
    CGameObject *g;   
    CEnemyBase *e;   
    CreateEnumeration(ENEMY_PRIORITY, ENEMY_PRIORITY + 10000, &pe);   
  
    while(g = pe.GetNext()){   
        e = dynamic_cast<CENEMYBASE*>(g);   
        // CEnemyBaseが基底クラスに無ければ処理はしない   
        if(e == NULL) continue;   
  
        if(e->HitTest(this)){   
            // 敵にヒットしたら弾を消去   
            RemoveObject(this);   
            return;   
        }   
    }   
    sprite.Draw(x, y, angle);   
}  
------------------------------------------------

>>派生クラスで追加した変数とか関数にアクセスしたい時はどうすれば良いのでしょうか?
派生クラス型のポインタを受け取れば、そのポインタを介して
派生クラスのメンバ関数やメンバにアクセスできると思います。
(private属性ならば、アクセッサを定義すればその関数からアクセス出来ます。)

また、基底クラス型のポインタの戻り値に、dynamic_castをすれば派生型のポインタに変換できます。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 16:13
by skyblue
投稿した後に、
龍神録プログラミングの館を参考に
作ろうとしているのならば、ソースを見てきましたけど、
main.cpp(project.zip/project/60章/mydat/source/)に処理を書いて、
それ以外はdefine.hやstruct.hにしろ、
project.zip/project/60章/mydat/source内にもソースがありますけど、
実体はmain.cppなどのcppファイルで書いているみたいです。
それ以外は、実体のない宣言などをしているファイルみたいです。
それ以外の章も同じだと思います。

>これの意味がさっぱりわからないです
>どういう事か詳しく説明してください・・
つまり、ボスの表示だけでひとつの関数を作るのです。
main関数で呼び出す形にすればメインファイルはかなりすっきりします。
敵の弾にあたったときの自機の当たり判定は
敵の弾の表示の関数(サブルーチン)に記述するときに
自機の座標を引数として指定すればできます。
ただしほとんどのサブルーチンがvoidになってしまいますが。

↑の環境はBCCでの場合です。それ以外は適宣VC++などの環境に合わせて変更してください。

補足

#includeはその場所にソースをそのまま張り付けているだけみたいです。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 18:11
by ねこ
皆さんの良レスに対してチルチルさんが「??」状態になってる気がします。
しっかりとクラスの基礎が分かってない状態で連続でレスが付いてる状態なので仕方無いとは思うのですが・・・

僕はチルチルさんにクラス解説サイトを熟読するか書籍でも買って頂いてしっかりと基礎を付けてもらって再度質問してもらうのが一番の近道だと思います。

提案を出してもそれを自分の中の最良に近づけようとしている点が問題なので
継承時の基底クラスポインタへの設定についてやgetter/setterの理屈、クラスにおけるスコープ、ポインタについて等、
色々学んで貰わないと話がかち合わないと思います。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 21:24
by チルチル
え~と前後の章も読んだら概要は理解できました

判定を行うクラスを作成し
基底クラスで使用し
自機やボスが継承し
必要ならばオーバーライドする

この構造はかなり便利そうですね
何事もすぐに投げ出してはいけないようです
これは反省しないといけませんね・・

>typeid演算子を使えば、基底クラス型のポインタにどのオブジェクトが入っているかを
>調べる事ができますよ。

そんな演算子があるとは知りませんでした・・
でも派生先のメンバを呼び出すにはヘッダのインクルードが必要のようですね

>なので、"世界"クラスに処理を持たせる、ではなく"世界"クラスから、
>"キャラクター管理"クラスを派生させ、そのキャラクター管理クラスの中で処理をする、

確かに性質の近いものをグループにして
それをさらにグループにするという構造の方が取っ付きやすそうですね

入力や効果音は・・
とりあえずイベント管理クラスとでも呼んでおくとして
世界の中にキャラクターとイベントを包含して
その中に自機や効果音を包含すると構造がわかりやすいでしょうかね・・

ボスが効果音を鳴らすメッセージを発信する時は

ボスのメンバ関数からキャラクターのメンバ関数を呼ぶ
キャラクターのメンバ関数から世界のメンバ関数を呼ぶ
世界のメンバ関数からイベントのメンバ関数を呼ぶ
イベントのメンバ関数から効果音のメンバ関数を呼ぶ

見たいな感じでしょうかね・・
連鎖が多くなっていますが、ボスが自機に何かする場合は
同じグループ内なので世界まで行かなくても良さそうですね
ボス→キャラクター→自機で行けそうです

そうすると
ボス→世界→効果音と直接たどっても良いかもしれませんが
メンバに直接アクセスするのはダメですかね・・

>龍神録プログラミングの館を参考に
>作ろうとしているのならば、ソースを見てきましたけど、

う~ん素材や雰囲気は参考にしていますが
コードは参考にしていないですね・・

>つまり、ボスの表示だけでひとつの関数を作るのです。

Boss.Run();

を分解して

Boss.Draw();
Boss.Move();
Boss.Attack();

見たいにするって事でしょうか?

>色々学んで貰わないと話がかち合わないと思います。

色々なサイトを見ているんですが
最低限の事が書いてあるサイトと応用的な事が書いてあるサイトはあるんですが
その中間がなかなか見つからなくて手間取っています・・

しかし継承は使えるのか半信半疑で敬遠していたんですが
やはり使った方が良さそうですね

Re:クラスのプロトタイプ宣言

Posted: 2009年10月08日(木) 23:49
by たいちう
そんなに気負わずに、「ボスと自機がいて、自機が撃った弾がボスに当たればクリア」
というプログラムを作ってみましょう。大作を思い浮かべて心配するよりも、
よほど多くの事を学習できるはずです。

開発に着手する前にある程度設計をしっかりやらないと、と思っていませんか?
開発の経験が十分無いと、しっかりした設計はできないのです。
掲示板の回答でも不十分に感じるでしょ?
チルチルさんが実現したいことをうまくまとめることができるならば、
他の人に設計を頼むことも可能といえば可能ですが、それじゃ面白くないでしょ?

ウォーターフォール型ではなく、プロトタイプ型かスパイラル型の開発をしましょう。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月09日(金) 12:46
by Mikan
私も同じ様な事を疑問に思ったことがあります。

たいちうさんの言うように、プレイヤー・ボスクラスを作成して
main関数から、クラスの制御(移動・ショット)などが呼べるのを確認して、カプセル化できるところはしていくといいかと思います。

当たり判定もmain関数(または世界クラス?)でやったほうが楽ですよ♪

そのプレイヤーやボスのクラスで、当たり判定などすべてのことをすると、すごくクラス間の連結が強くなってしまい、あんまりクラスの恩恵はないかも^^

Re:クラスのプロトタイプ宣言

Posted: 2009年10月09日(金) 17:45
by チルチル
前回はボトムアップ型で失敗して
今はウォーターフォール型で難航しているので
とりあえずスパイラル型に移行してみましょうかね・・

製作に入れないのは歯痒いですが
ここでしっかり理解しておいた方が
後々役に立つと思うので慌てずに行って見ます

>main関数から、クラスの制御(移動・ショット)などが呼べるのを確認して、カプセル化できるところはしていくといいかと思います。

どのくらいをカプセル化と言うのか良くわからないんですが
包含されているクラスは外側から管理されるのでカプセル化は難しいでしょうね
これは一体化していると考えてmainに対して世界をカプセル化した方が良さそうですね
とりあえずメインループも世界の中に入れてRun以外は非公開でしょうかね・・
そうすれば全体としてのカプセル化は保たれますし

>当たり判定もmain関数(または世界クラス?)でやったほうが楽ですよ♪

それは私も思いましたね
Kitei*型のポインタを2個渡せば衝突しているか返すようなメンバ関数を世界に追加して
世界の方で勝手に判定して一方的に接触連絡した方が直感的でしょうかね・・

そうなるとクラス内に線形リストを持つ弾とかザコ敵はまた専用の関数を追加するとしましょう
弾同士とか敵同士は直接参照を許可しても良いでしょうかね・・

>そのプレイヤーやボスのクラスで、当たり判定などすべてのことをすると、すごくクラス間の連結が強くなってしまい、あんまりクラスの恩恵はないかも^^

う~んクラスの恩恵は今の所コンストラクタが大きいと思いますね
だから動的にインスタンスを作成するのはしない方向で考えています

まあこの辺は理解してから考えた方が良いですね・・
とりあえずわかった範囲でコードを組んでみます

Re:クラスのプロトタイプ宣言

Posted: 2009年10月09日(金) 18:39
by GPGA
個人的には当たり判定マネージャーを作ってそこで判定させるのも
面白いと思います。
以下、当たり判定マネージャーの例です。(コンソールアプリで動きます。)
#include <iostream>
#include <list>

typedef void (*HitCallBack)(void*);
struct Rect {
	Rect(){}
	Rect(int x, int y, int w, int h) {
		this->x = x;
		this->y = y;
		this->w = w;
		this->h = h;
	}
	int x, y, w, h;
};

class HitManager {
	struct Data {
		Rect rect; // あたり範囲
		HitCallBack callBack; // 当たった時に呼び出される関数
		void* param; // 関数のパラメータ
	};

	std::list<Data> dataLeft_;
	std::list<Data> dataRight_;
public :
	void Clear() {
		dataLeft_.clear();
		dataRight_.clear();
	}
	void AddLeft(const Rect& rect, HitCallBack callBack, void* param) {
		Data data;
		data.rect = rect;
		data.callBack = callBack;
		data.param = param;
		dataLeft_.push_back(data);
	}
	void AddRight(const Rect& rect, HitCallBack callBack, void* param) {
		Data data;
		data.rect = rect;
		data.callBack = callBack;
		data.param = param;
		dataRight_.push_back(data);
	}

	void Check() {
		std::list<Data>::iterator it1 = dataLeft_.begin();
		for (; it1 != dataLeft_.end(); ++it1) {
			std::list<Data>::iterator it2 = dataRight_.begin();
			for (; it2 != dataRight_.end(); ++it2) {
				// あたり判定
				const Rect& rect1 = it1->rect;
				const Rect& rect2 = it2->rect;
				if ((((rect1.x - rect2.x - rect2.w) & (rect2.x - rect1.x - rect1.w) & (rect1.y - rect2.y - rect2.h) & (rect2.y - rect1.y - rect1.h)) < 0)) {
					// 当たっていた場合コールバック呼び出し
					it1->callBack(it1->param);
					it2->callBack(it2->param);
				}
			}
		}
	}
};

class Kitai {
public :
	virtual ~Kitai(){}
	virtual void Run(HitManager*) = 0;
};

class Player : public Kitai {
	// 当たった時の処理を記述
	void HitCallBack_() {
		std::cout << "プレイヤーあたった" << std::endl;
	}
	static void HitCallBack(void* p) {
		((Player*)p)->HitCallBack_();
	}
public :
	void Run(HitManager* hitManager) {
		// あたり判定クラスに登録
		hitManager->AddLeft(Rect(0, 0, 10, 10), HitCallBack, this);
	}
};

class Boss : public Kitai {
	// 当たった時の処理を記述
	void HitCallBack_() {
		std::cout << "ボスあたった" << std::endl;
	}
	static void HitCallBack(void* p) {
		((Boss*)p)->HitCallBack_();
	}
public :
	void Run(HitManager* hitManager) {
		// あたり判定クラスに登録
		hitManager->AddRight(Rect(0, 0, 10, 10), HitCallBack, this);
	}
};

int main() {
	Player player;
	Boss boss;
	HitManager hit;

	// 各オブジェクトの処理を実行
	player.Run(&hit);
	boss.Run(&hit);

	// あたり判定チェック
	hit.Check();
}
 
Windows XP Professional
VisualStudio 2008 SP1にて作成

Re:クラスのプロトタイプ宣言

Posted: 2009年10月09日(金) 18:59
by MNS
実際にゲームを作ってみて、クラスのメリット・デメリットを知ることも良いと思います。
やはり、開発の意欲は自分が作りたいものを作っているときが一番沸きますからね。
恐らくはどこかで躓き、一から設計を見直すことになるでしょうが、それも経験です。
もちろん基本的なことは学んでおくべきでしょうけれど。
ただ、自分で解決することが非常に重要です。
わからないからといって、直ぐに質問するのでは意味がありません。

メンバがプライベートならカプセル化の原則は守られていると言えます。
class Object
{
private:
	int x;
public:
	int GetX(){ return x; }
	void SetX(int a){ x = a; }
};
このように、一つのメンバ変数に対し、(必要であればですが)セッターおよびゲッターを用意するだけで、
たとえSetX関数でxの値が変えられるとしても、カプセル化の原則は守られます。
なので、基本的に包含されるクラスであろうと、全てのクラスに対しカプセル化は考慮すべきです。

確かに、当たり判定は自機および敵機を包含するクラス、いわゆる世界クラスで行ったほうが楽かもしれませんね。
ただ、あまりに世界クラスが肥大化してしまうようでは、当たり判定を行うクラスというものを用意することも考えたほうがいいかもしれません。

>弾同士とか敵同士は直接参照を許可しても良いでしょうかね・・
これはどういうことでしょうか?

コンストラクタは、カプセル化が守られてこそ初めてメリットを発揮します。
>だから動的にインスタンスを作成するのはしない方向で考えています
コンストラクタのメリットが大きいので動的なインスタンスの作成をしない、
というのは良く分かりませんが、動的なインスタンスの作成は考えていたほうがいいかもしれません。
というのも、継承および多態性では必要となるかもしれないからです。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月09日(金) 22:34
by チルチル
>個人的には当たり判定マネージャーを作ってそこで判定させるのも
>面白いと思います。
>以下、当たり判定マネージャーの例です。(コンソールアプリで動きます。)

関数も型にできるんですね・・
struct Rect {
	Rect(){}
	Rect(int x, int y, int w, int h) {
		this->x = x;
		this->y = y;
		this->w = w;
		this->h = h;
	}
	int x, y, w, h;
};
これって後ろで定義しているint x, y, w, h;が
Rectから見えるのは何ででしょうか?

コールバックと言う方法は知りませんでしたね・・
どこかで使う時があるかもしれません

まあ自機とかボスの中でやっても良いんですが
ちょっと問題があるんですよね・・

例えば「自機がボスに接触したかの判定は自機とボスのどっちでやるのか?」という問題です

別にどっちでも特に変わらないと思いますが
私としては迷う所なんですよね・・

だから世界の方でやった方が後草無くて良いかな~と思うんですよ・・

>このように、一つのメンバ変数に対し、(必要であればですが)セッターおよびゲッターを用意するだけで、
>たとえSetX関数でxの値が変えられるとしても、カプセル化の原則は守られます。

う~んゲッターとかではなくてポインタを取得して間接参照するような感じで行こうと思ったんですが
それだとカプセル化が崩れますか?

>なので、基本的に包含されるクラスであろうと、全てのクラスに対しカプセル化は考慮すべきです。

やはり世界→キャラ→自機と包含していたとしても
世界から自機を直接参照するのはダメみたいですね・・

良く考えたら世界クラスの外にはmain関数ぐらいしか無いと思うから
クラス内でのカプセル化の方を重視した方が良さそうですね・・

>確かに、当たり判定は自機および敵機を包含するクラス、いわゆる世界クラスで行ったほうが楽かもしれませんね。
>ただ、あまりに世界クラスが肥大化してしまうようでは、当たり判定を行うクラスというものを用意することも考えたほうがいいかもしれません。

やはり自機クラスやボスクラスはキャラクラスに包含して
キャラクラスを世界クラスが包含する方が良さそうですね

自機からボスへの判定なら同じグループなのでキャラクラス内で判定をやってしまえば
世界クラスの負担が減るし構造が明確になりそうです

それなら世界クラスにはグループをまたぐ判定だけをやってもらえば良さそうですね

まあ判定に関してはクラスなりメンバ関数なりで汎用化するとして
今回は普通の関数は使わないでメンバ関数だけでやってみます

>これはどういうことでしょうか?

弾の構造で思い付くのは

弾管理クラスの中で弾の線形リストを作る
弾クラスの線形リストを作る

の2つです
私としては前者を使おうと思っているのですが
それだと弾同士が自由に参照できます
これはアリなのか微妙なんですよね・・

まあポインタを使った間接参照を多用する場合は
線形リストなんだからどっちにしても間接参照になると思うから
どっちでも関係無いようにも見えますが・・

>コンストラクタのメリットが大きいので動的なインスタンスの作成をしない、
>というのは良く分かりませんが、動的なインスタンスの作成は考えていたほうがいいかもしれません。
>というのも、継承および多態性では必要となるかもしれないからです。

コンストラクタで読み込みをやる予定です
それはあんまり関係ないですが
動的にインスタンスすると管理が大変な気がするんですよね・・

選択画面からゲーム画面に飛ぶ時に選択画面を開放すると
次に選択画面をインスタンスする時に前回どこを選択していたのかわからないと言う問題も出ますし・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月10日(土) 00:43
by conio
>>選択画面からゲーム画面に飛ぶ時に選択画面を開放すると 
>>次に選択画面をインスタンスする時に前回どこを選択していたのかわからないと言う問題も出ますし・・
消さなければいいんですよ。
というより、そういったシーケンス遷移は木構造を思い出してください。
階層が深くなっても、根元から辿って行く方法です。

----------------------------------
タイトル
 ┃
┏┻┓
選 終
択 了
画
面
┣┳┓
初続終
めきわ
かかり
ららか
┃┃ら
┃┃┃
オコエ
|ンン
プテデ
ニィィ
ンニン
グュグ
 |
----------------------------------
例えば、"選択画面クラス"には下層の"ゲームプレイクラス"を呼び出して実行するようにするのです。
(ここではOP、Continue、Endingの3種類)

で、"ゲームプレイクラス"の処理が終了した時、(主人公が死んだりなど)
"ゲームプレイクラス"をデストラクトし、また"選択画面"クラスに戻ってくるようにします。

要するに、デストラクトするのは末端からです。
現在のシーケンス状態以外のクラスは存在しない(全てデストラクトしている)
という考え方はよろしくないと思います。

タイトルクラスが、選択画面クラスを呼んで、選択画面クラスがゲームプレイクラスを・・・
というように、根元から全てが繋がっており、ひとつの纏まりとなっているイメージです。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月10日(土) 13:10
by チルチル
そういえば構造的にはそんな感じですよね・・

前回質問した時に画面の移行は多態性を利用したメンバ関数ポインタを使って
どの画面へ飛ぶかは列挙型で指定しようと思ったんですが
それだとクラスは動的にインスタンスできないですね・・

でも包含は想定していなかったので構造は多少変更は必要ですね・・

選択画面なども世界が包含する事になると思うので
世界の中でうまく場合分けしないといけませんね・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月10日(土) 20:57
by MNS
オブジェクト指向言語には、デザインパターンと呼ばれる幾つかのテクニックがあり、
その中にステート(State)デザインパターンと呼ばれるものがあります。
これは条件分岐や列挙体を使わないので、とても簡潔に書くことができます。
選択画面で応用すればとても便利ですので、一度調べてみてはどうでしょう?

Re:クラスのプロトタイプ宣言

Posted: 2009年10月11日(日) 00:14
by GPGA
>これって後ろで定義しているint x, y, w, h;が
>Rectから見えるのは何ででしょうか?
C++の仕様です

>まあ自機とかボスの中でやっても良いんですが
>ちょっと問題があるんですよね・・
HitManagerのCheckの呼び出しはWorldで行います。

もし今回の当たり判定をHitManagerを使用しないで記述した場合
Worldの中の当たり判定とその後の処理がどのように記述され
HitManagerを使用した場合、どのようになるかを考えてみてください。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月11日(日) 18:08
by チルチル
包含の練習をしてみたんですが
class Boss {
private:
public:
	void Run(void);
};
class World {
private:
	Boss Boss;
public:
	int i;
};
World World;
void Boss::Run(void){
	World.i=0;
}
実際にやってみると包含であって内部クラスではないので
宣言部では独立している事になってしまうんですよね・・

世界からボスへはメンバを参照しているだけですが
ボスから世界へは別のクラスのメンバにアクセスしているので
どういう風に考えたら良いのか模索中です

このコードだと名前関係でエラーが出るので
型名と実体が同名なのは何とかしないといけませんね・・

>根元へ行けば行くほどにオブジェクトはそれ1つで完結する独立したものになります。
>そうしないと、機能をカプセル化する意味が全くありません。

これの意味がやっと分かりました、世界は自機やボスを包含しているので
世界が自機やボスを参照するのは自分のメンバにアクセスしているだけなので単体で完結している事になります
自機やボスが世界を参照するのは他のクラスにアクセスする事になるので独立性が低くなります
確かに根本に行くほど外部へのアクセスが少なくなりますね

あと練習中に意味不明なエラーが出てしまいました
class World {
private:
public:
	void run(void);
};
void World::run(void){
}
これはコンパイルが通るんですが
class World {
private:
public:
	void Run(void);
};
void World::Run(void){
}
runをRunに変えると

error LNK2005: "public: void __thiscall World::Run(void)" (?Run@World@@QAEXXZ) は既に main.obj で定義されています。
fatal error LNK1169: 1 つ以上の複数回定義されているシンボルが見つかりました。

このようなエラーが出るんですよね・・

他のプロジェクトでは出ないので
これから書き込むために空のソースとかヘッダを
沢山用意しているのが怪しいんですが
何がいけないのかわからない状態です・・

しかも引数をint型にするとコンパイルが通るので
さらに良くわからないです・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月11日(日) 18:50
by kazuoni
うーん。ねこさんや他の方々からも意見がありましたが、
ちょっといろいろ論議する前に、
先人の書かれているものを一度読んだほうがいいかと思います。(参考書・参考ページ)
No:40290の書かれているコードは基礎を学んだ人はすぐにおかしな所に気が付きます。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月12日(月) 12:18
by チルチル
実際にやってみると問題点が良くわかりますね・・

非公開メンバにはポインタを使ってもアクセスできない事がわかりました
だとすると公開しておくしかないんでしょうかね・・
カプセル化が壊れますけど別に関数を用意するのは無駄が多いし・・

自機とかボスは良いとして弾なんかは
弾と世界で別々にループを回すと時間が掛かるので
枝先でやった方が良いかもしれませんね・・

良く考えたら
世界→キャラ→自機
と参照しないとカプセル化が壊れますけど
自機→世界
と参照するのは別に問題ない気もしますね・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月12日(月) 12:48
by バグ
>>カプセル化が壊れますけど別に関数を用意するのは無駄が多いし・・

私も当初はこの作業を無駄だと思っていましたが、規模の大きい物を組むようになると、アクセッサのありがたみが理解できるようになりました。特にメンテナンスや改造の容易さにおいて痛感するのではないかと思います。

無駄だと思うかもしれませんが、アクセッサを用意して、カプセル化の原則を守ることによるメリットの方が遥かに大きいですよ。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月12日(月) 14:02
by チルチル
やっぱり構造は明確な方が良いみたいですね

となると世界まで行かなくて良い場合でも世界まで行った方が良さそうですね
むしろ包含の階層を増やさない方が良いかもしれません
世界の負担は増えますけどオーバーヘッドは減るかもしれませんし・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月13日(火) 01:02
by チルチル
いちいちアクセッサを作るのは無駄だと言うお話もありましたが
現実的にポインタを使ってもアクセス拒否されるので
やはりアクセッサを使うしかなさそうですね

そうするとインスタンスを基底クラスのポインタで持つ必要もなくなりますからね
動的キャストは処理が重いらしいですし、静的キャストの方を使った方が良さそうです

そういえば内部へのカプセル化は良いとして
外部へのカプセル化はすべきなんでしょうか?
世界が自機のメンバ変数に直接アクセスしてはいけないのはわかりますが
自機が世界のメンバ変数に直接アクセスするのもダメなんでしょうか?

むしろ相手によって公開する範囲を指定できないのが残念ですね・・

Stateについては解説サイトを探しています
今の所は他で動的なインスタンスはしないつもりですね

ちなみに添付したコードですが
改良すべきなのはどの辺りでしょうか?

Re:クラスのプロトタイプ宣言

Posted: 2009年10月13日(火) 03:53
by kazuoni
>ちなみに添付したコードですが
>改良すべきなのはどの辺りでしょうか?

人それぞれですので、こうしろとは言えない気がしますが・・・

・自分はProtocol.hはやめたほうがいいかと思います。
確かに、コードの見栄えはいいかもしれないですけど、
ヘッダーが増えるにしたがって、
コンパイルの時間がものすごく長くなったりします。
各所でそれぞれ必要なヘッダーをインクルードするのを自分は採用しています。

・Charクラスで座標をpublicにしていますが、
自分ならprivateにします。

・ShotClassのコンストラクタなどでの動的確保は、
例外処理まではいかなくても、
さすがに確保失敗時の動作は書いたほうがいい気がします。

・グローバルのWorldClass World;を使用しないですむ構造にする

・DxLib_Init()とDxLib_End()を同じクラスで管理

などなど。
なんだか、すべてをクラスに混ぜている気がします。
「自分」は(何度も「自分」を押しますが^^;)ですが、
再利用を念頭にクラス化します。
確かにシングルトンクラスも使用はしますが。。
クラスのおかげで「管理しやすく」ではなく、「管理しにくく」
なってしまっては、元も子もないような気がします^^;

まだ大規模な開発をしたことがないので、
恐らくあてにならないことを言ってる気がしますが・・・
あくまで一個人としての意見なので、参考までに^^;

Re:クラスのプロトタイプ宣言

Posted: 2009年10月13日(火) 13:15
by チルチル
>自分はProtocol.hはやめたほうがいいかと思います。

何でコンパイルにこんなに時間が掛かるのかなと思っていたら
やっぱりアレは問題でしたか・・

しかし世界クラスは必ずインクルードする必要があると思いますが
世界クラスは自機やボスのポインタを持っているので
そっちもインクルードしないと宣言ができなくなってしまって
結局すべてインクルードする事になってしまいますね・・

ポインタを基底クラスで持つメリットってこういう時なんでしょうか?
しかし動的キャストは処理が重いのでダメですね・・

最初の方で教えてもらった
class B;
これが使えるかもしれませんね
枝先が直接アクセスする事は無いと思うので
クラスのポインタと言う事がわかれば
宣言は可能かもしれません
世界本体ではどうするのか微妙ですが
そこはやってみれば何とかなるはず・・

>Charクラスで座標をpublicにしていますが、

アクセッサを作る事にしたので非公開にした方が良いですね
でも継承専用なのでprivateだとサブクラスからも見えないのでprotectedの方が良いかな・・
基底クラスの段階でアクセッサを作るならprivateでも良いですが
自分のメンバを変更するのにアクセッサを使うのも変だからやっぱりprotectedでしょうかね・・

>ShotClassのコンストラクタなどでの動的確保は、

newがNULLを返した時に終了するとかでしょうか?
いちいち書くのも大変なのでマクロ化が必要かもしれません・・

>グローバルのWorldClass World;を使用しないですむ構造にする

そういえば変ですね・・
WorldClassのメンバを全部staticにすれば良いかもしれません
その場合はWorldClassは長いのでWorldにする必要がありますね・・
むしろ全部のクラスをstaticにしても良いかもしれませんね
世界クラスでポインタを作る必要も無いですし・・
でもそれだとカプセル化が壊れるし
アクセッサをどうすれば良いのかわからないのでダメですね・・

>DxLib_Init()とDxLib_End()を同じクラスで管理

DxLib_End()は世界のデストラクタに入れた方が良いですね・・

>再利用を念頭にクラス化します。

クラスの再利用ってピンと来ないんですが
どんな風に再利用するのでしょうか?

>確かにシングルトンクラスも使用はしますが

こういうデザインパターンもあったんですね・・
インスタンスが1つしか作成されないのを保障するのもダメ押しで良いかもしれませんが
全部のクラスをstaticにした場合のアクセッサがさらに良くわからなくなるので微妙ですね・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月13日(火) 17:11
by MNS
>しかし世界クラスは必ずインクルードする必要があると思いますが
>世界クラスは自機やボスのポインタを持っているので
>そっちもインクルードしないと宣言ができなくなってしまって
>結局すべてインクルードする事になってしまいますね・・
言ってる意味が良く分からないのですが、
world.cppでboss.hやplayer.hをインクルードしていたとしても、
world.hで何もインクルードしていなければ、player.cppでworld.hをインクルードしても、
boss.hやその他のヘッダはインクルードされないと思いますが。

>動的キャストが遅い
確かにスタティックキャストと比べると遅いかもしれませんが、
問題になるほど処理が重いわけではないと思います。少なくともnewの方が遅いでしょう。

>WorldClassのメンバを全部staticにすれば良いかもしれません
>その場合はWorldClassは長いのでWorldにする必要がありますね・・
>むしろ全部のクラスをstaticにしても良いかもしれませんね
>世界クラスでポインタを作る必要も無いですし・・
>でもそれだとカプセル化が壊れるし
>アクセッサをどうすれば良いのかわからないのでダメですね・・
WorldClassのメンバを全てstaticにするというのも手ですが、
それならばシングルトンを実装してしまったほうが話しは早いでしょう。
自機やボスが常にWorldへのポインタをもつのも一つの手ですし、
class A
{
public:
	static A* instance;
	A(){ instace = this; }
}
こうやって、静的なパブリックメンバとして、自身のポインタを用意し、
コンストラクタでインスタンスが作成される際に自身のアドレスを代入する、
というやり方もあります。ただし、インスタンスが一つのみである場合に有効です。

>クラスの再利用
テンプレートという機能を学べば、少しはわかるかもしれません。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月13日(火) 18:39
by チルチル
>world.cppでboss.hやplayer.hをインクルードしていたとしても、
>world.hで何もインクルードしていなければ、player.cppでworld.hをインクルードしても、
>boss.hやその他のヘッダはインクルードされないと思いますが。

え~とヘッダファイルにはクラスの本体を、ソースファイルにはメンバ関数の中身を書きますよね
と言う事はWorld.hでBoss.hやPlayer.hをインクルードしないと
WorldクラスのメンバにBoss*型やPlayer*型のポインタを宣言できないんじゃないのかなって事です

>確かにスタティックキャストと比べると遅いかもしれませんが、
>問題になるほど処理が重いわけではないと思います。少なくともnewの方が遅いでしょう。

気にするほどでも無いんでしょうかね・・
だとしても動的キャストは構文が長いのでマクロ化する必要がありますね・・

>WorldClassのメンバを全てstaticにするというのも手ですが、
>それならばシングルトンを実装してしまったほうが話しは早いでしょう。
>自機やボスが常にWorldへのポインタをもつのも一つの手ですし、

そういえばstaticにするとstaticメンバしかアクセスできなくなりますからね・・
全部のクラスをシングルトンにしておくのもアリかもしれません
確か2回目以降は普通にポインタを返すようになっているわけだから
ポインタを保持しなくても毎回呼び出せば良いかもしれませんね
毎回作成されているかの分岐を通ると無駄が多いですが
関数ポインタへの代入とか一回しか呼び出さない物との互換性のために
そこはしかたないでしょういかね・・
まあ継承とオーバーライドで何とかなるかもしれませんが
そうすると間接参照時に構文が長くなるのでダメですね
Worldへのポインタを基底クラスで宣言するのも良いかもしれません

Re:クラスのプロトタイプ宣言

Posted: 2009年10月13日(火) 18:58
by GPGA
>と言う事はWorld.hでBoss.hやPlayer.hをインクルードしないと 
>WorldクラスのメンバにBoss*型やPlayer*型のポインタを宣言できないんじゃないのかなって事です 
いいえ

------------------ World.h ----------------------
class Player;
class Boss;

class World {
private :
	Player* player_;
	Boss* boss_;
};

------------------ World.cpp ----------------------
#include "World.h"
#include "Player.h"
#include "Boss.h"


・・・
 

Re:クラスのプロトタイプ宣言

Posted: 2009年10月13日(火) 19:07
by MNS
>え~とヘッダファイルにはクラスの本体を、ソースファイルにはメンバ関数の中身を書きますよね
>と言う事はWorld.hでBoss.hやPlayer.hをインクルードしないと
>WorldクラスのメンバにBoss*型やPlayer*型のポインタを宣言できないんじゃないのかなって事です
ポインタならば可能だと思います。と、いうか、チルチルさんのコードを見る限り、
World.h内ではBoss.hおよびPlayer.hをインクルードしていませんが、
Boss型やPlayer型のポインタを宣言しているように見受けられるのですが。

なんというか、チルチルさんの考えは短絡的すぎやしませんか?
全部のクラスをシングルトンにするなんて、せっかくのオブジェクト指向が台無しになります。
シングルトンはグローバル変数と同じような振る舞いをしますから、
グローバル変数特有のデメリットが浮き出てきます。
基本的にC++においてグローバル変数を使うのは好まれません。
WorldClassにシングルトンを実装するのは、WorldClassが全クラスからアクセスできることによって
もたらされるメリットが、それにより引き起こされるデメリットよりも重要だからです。
全部をシングルトンにしてしまえば、
結局はPlayerクラスやBossクラスがみんなグローバル変数だった時の状況に戻ってしまいますし、
そうするとWorldClassをシングルトンにする理由もなくなってしまいますよ。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月13日(火) 19:14
by GPGA
>World.h内ではBoss.hおよびPlayer.hをインクルードしていませんが、
>Boss型やPlayer型のポインタを宣言しているように見受けられるのですが。
これが通るのは、World.cpp内部でインクルードしているProtocol.hが
Boss
Player
World
の順にインクルードしているため、文法エラーにならないだけです。
cppのインクルードする順番に依存する良くないつくりです。(企業が出すライブラリでもたまにあるから困る)

main.cppの中でworld.hだけを単独でインクルードすれば、文法エラーになるでしょう。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月13日(火) 20:24
by チルチル
これは使えそうですね、宣言にはこれを使うとして
うっかり「枝先は根本を見る」という前提を忘れていました・・
自機やボスは世界だけを見る事に戻して
枝先のソースは自分のヘッダとWorld.hだけをインクルードして動くようにした方が良さそうですね

世界→キャラ→自機と包含していても自機から直接世界を参照しますから外側へのカプセル化は壊れますが
こうした方がChar.GetBossとかWorld.GetBossが混在するのを防げるので良いかもしれません
内側へのカプセル化は守らないといけないので世界はキャラを通して自機を参照しますが
世界の負担が増えますが枝先からは見えないので問題無さそうですね
キャラは包含と言うよりグループと考えた方が良いかもしれません
枝先からのリクエストは世界が受け取って実際の処理はグループに委託すると後から変更しやすくて良さそうです
この構造だと行きは簡単だけど帰りが複雑って事になりますけど
根本が複雑になるデメリットよりも枝先が簡単になるメリットの方が大きそうだから悪くないかもしれません・・

シングルトンは複数のインスタンスが作成されない事を保障するものですが
別に自分で複数作らなければ問題は無いでしょうかね・・
>グローバルのWorldClass World;を使用しないですむ構造にする
これはシングルトンで実現できるんでしょうか?
staticならクラス名にスコープ演算子でアクセスできると思いますが
シングルトンの場合はどうやってアクセスするんでしょうか?
枝先がWorldのポインタを保持するやり方もアリですが・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月14日(水) 12:41
by チルチル
良く考えたらインクルードするヘッダを構造から限定できそうですね

上の構造だとすると

・自機は世界が見える
・世界はキャラが見える
・キャラは自機が見える

と言う風にすれば構造が明確になって世界の負担も減りますね
何だかスコープの循環リストみたいにも見えます
これは全体的にバランスが良さそうな気がしますね

あと解説サイトでデザインバターンを見てきました
javaで書かれていたんですがアルゴリズムは理解できたと思います

Stateは添え字を変えるのではなく中身を変えるようですね
どこか関数ポインタを連想させます
現在の状態以外は存在しないのはダメとして
複数組み合わせるのも微妙なので
最初に全部インスタンスしておいて
場面を変える時に次のクラスのポインタを
代入すれば綺麗に変化してくれそうです

ポインタ名が列挙型の変わりになるのでかなりシンプルになりそうです
分岐内で変更するのは怖いので抜けてから変更した方が良さそうですね
列挙型に比べて関連性が弱く見えますが、まあ使う側から見れば問題無いでしょうかね・・

しかしデザインパターンは凄いですね・・
もっと早く気付くべきだったです・・

Re:クラスのプロトタイプ宣言

Posted: 2009年10月15日(木) 00:26
by チルチル
そういえばクラスの再利用ですが

>テンプレートという機能を学べば、少しはわかるかもしれません

例えば2つの座標と半径を引数として渡すと接触しているかを返すような場合に
テンプレートを使ってどんな型の座標同士でも判定できるようにするとかでしょうか?
と言うよりこれは再利用と言うより汎用化と言うんでしょうか?

Re:クラスのプロトタイプ宣言

Posted: 2009年10月16日(金) 12:20
by チルチル
皆さんに色々と教えていただいたおかげで
良さそうな構造も浮かんだのでそろそろ製作を開始しようと思うのですが
上に書いたような構造で大丈夫だと思いますか?

Re:クラスのプロトタイプ宣言

Posted: 2009年10月16日(金) 12:57
by conio
作り始めて、初めて気づく問題点などもあると思うので、とりあえず製作を開始してみてはどうでしょうか。
その時に 適宜もう一度質問する形にした方が良いかと。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月17日(土) 01:21
by チルチル
なるほど~では製作を開始してみようと思います

皆様のおかげで大変勉強になったんですが
ここで少し疑問が浮かびました

解説サイトを見て思うにデザインパターンは定石ですがその他諸々に関しては
「何人かで作る場合に相手が使いやすくする」
「後から変更するのを容易にする」
これが重要だと言う事みたいですが

疑ってるわけではありませんが
例えば1人で製作していてコードも完璧で後から修正する必要が無い場合に
根本で枝先が繋がっているメリットは何なのでしょうか?

つまり1人で作るなら相手が使いにくても良いんじゃないか?
後から変更しないなら変更しにくい構造でも良いんじゃないか?って事です

Re:クラスのプロトタイプ宣言

Posted: 2009年10月17日(土) 01:37
by 御津凪
> つまり1人で作るなら相手が使いにくても良いんじゃないか?

例えば、自分一人で一つのプログラムを作り上げたとします。
しばらく後、そのプログラムのコードを参考(あるいは流用)しようとします。
その時自分がしっかりコードを覚えているのであれば問題ないかもしれませんが、
普通は忘れているはずです。

何が言いたいのかというと、自分の書いたコードでも、数週間後に見ると
書いたコードを忘れているのですから、他人のコードを見るのと変わりないということです。
つまり、使いにくいコードを書くことは自分でも使いにくいコードに成り得ます。

※他の方のコードの書き方を真似ると、
 今までに書いた自分のコードの書き方を忘れるということもあります。

そのため、出来るだけわかりやすく書くのが良いですね。
(コメントも書いておくといいです)

Re:クラスのプロトタイプ宣言

Posted: 2009年10月17日(土) 01:50
by たいちう
とっとと作り始めろ、という意見は私の他にもあったと思うけど、それは置いといて。

> つまり1人で作るなら相手が使いにくても良いんじゃないか?
> 後から変更しないなら変更しにくい構造でも良いんじゃないか?って事です

後から変更しないためには、作り始める時点で、完成形が分かっていないといけません。
規模が大きくなると、プロとはいえ凡庸な私には不可能な話であり、
失礼ながら私よりも(現時点では)腕が劣ると思われるチルチルさんの場合、
変更しないという前提で作り始めることは不可能ではないですか?

置いといて、とか言いながら、同じ事を書いていますね。
各人いろいろこだわりがあるわけで、それを否定するのも野暮かもしれませんが、
チルチルさんは失敗を恐れすぎているような気がします。

試行錯誤は必須ですよ。
私のプログラミングの時間の8割方は試行錯誤じゃないだろうか?多すぎ?

Re:クラスのプロトタイプ宣言

Posted: 2009年10月17日(土) 01:55
by 御津凪
> 私のプログラミングの時間の8割方は試行錯誤じゃないだろうか?

私はプログラミングの時間の半分位はコードの最適化にかけてますね^^;
それでも見やすいコードを維持しながらなので大変ではあります。
結局は試行錯誤の連続ですね。
昔も今も作りなおしは良くありますし。

Re:クラスのプロトタイプ宣言

Posted: 2009年10月17日(土) 16:37
by チルチル
う~ん作り始めたかったんですが間の悪い事にテスト期間中だったもので
しかも新型インフルエンザに感染してプログラムを組む余力がありませんでした・・

>つまり、使いにくいコードを書くことは自分でも使いにくいコードに成り得ます。

デザインパターンの中でFacadeが印象に残ったんですが
作る側が使う側のために1つに見せかける構造は世界クラスと似通っていますが
見せ掛けを作る手間が作る側に増えるから1人で作る場合は必要ないんじゃないかとも思ったんですが
自分で作ったものでも使いこなせない事もあるなら必要はありますね

>変更しないという前提で作り始めることは不可能ではないですか?

前提と言うよりは「変更せずに済んだ」と言う過去形をイメージしていたのですが
まとめると、定石を使わなくてもできるなら良いけど
できると思っていても実際にやってみるとできない事が多いから
定石を使った方が無難だと言う事ですね、納得しました

>チルチルさんは失敗を恐れすぎているような気がします
>私のプログラミングの時間の8割方は試行錯誤じゃないだろうか

それは言えてますね、私も9割方試行錯誤していますが
プログラム上ではなくて頭の中でやっているんですよね・・

6時間ぐらいアルゴリズムを考えた後に10分ぐらいプログラムを書いて・・みたいな感じです
さすがに考えてる時間が長いので実際にやってみても試行錯誤する所はほとんど無いんですが

何でこういう事になっているかと言うと
プログラムの中に「見えないタブ」とか「見えない空白」があるのがすごく嫌なので
できるだけ頭の中で試行錯誤を終わらせてプログラムを書いたり消したりする時間を減らして
うっかり「見えない諸々」が発生する確率を下げようとしているのですが

そんな事を気にしてる場合じゃないですね・・プログラム上で試行錯誤してみます

では今度こそ解決にします

皆様長い間ありがとうございました