アプリ版:「スタンプのみでお礼する」機能のリリースについて

ゲームプログラミングを勉強中の者です。
落ち物パズルゲームを作ろうとしているんですが、
現在、クラスの設計を
ゲームステージのクラスがあり、そのクラスのメンバとして各プレイヤーのクラスを入れている、つまり

//プレイヤーのクラス
class Player{
(ステージなどのデータのメンバ変数)
void showStage(); //このあたりが質問の内容です
void update();
};
// ゲームステージのクラス
class Stage{
Player p; //StageクラスのメンバとしてPlayerを含める
(その他ステージのメンバ変数)
void play(); // メインの関数
};
のようになっていて、
実行の際はStageクラスのplayの中にゲームの内容(入力、更新、表示のループなど)が全て入っていて、playの中で別の関数を呼び出したりしているのですが、
プレイヤーごとに処理した方が楽なもの、例えばプレイヤーごとの入力などは、今は関数playの中で
void Stage::play(){
(中略)
p.update();
(中略)
}
のように、StageのメンバのPlayerクラスのpからp.xxxという形で呼び出しています。

しかし、最近読んだ本では、
playerクラスはnewで作成して、こういう関数は参照渡しで実行する、つまり
class Player{
(略)
};

class Stage{
void player_update(player* p);
 (略)
void play();
};

void Stage::play(){
(中略)
Player* p = new Player(); // プレイヤークラスを作成し
(中略)
player_update(*p); // 参照渡しする
(中略)
}
とするべきのようなことが書いてありました。
updateの中ではかなりplayerのメンバ変数を参照・変更したりするのですが、両者の方法では後者の方が速さの面で有利なのでしょうか?
違いがあるとすれば、どの程度速さに影響するのでしょうか?

ポインタを最近使い始めたばかりで記述自体合っているかあまり自信がないのですが、宜しくお願いします。

A 回答 (5件)

class Stage{


void player_update(Player* p);
 (略)
};

上記はは参照渡しではなくポインタ渡しです。
参照渡しであれば

class Stage{
void player_update(Player& p);
 (略)
};

となります。
(参照はその名の通り参照なので、ポインタと違いNULL値を含めアドレス値を代入する事ができません。)

参照でPlayerをメンバーで持つ場合は以下のようになります。(コンストラクタの引数で参照を渡す必要があります。)

class Stage{
Player& p; //StageクラスのメンバとしてPlayerを含める
public:
Stage( Player& _p ):p(_p){}
};

仮に参照渡しでもポインタ渡しでも無く実体を渡すのが最も悪い例です。
class Stage{
Player p;
void player_update(Player _p){ p = _p; }
 (略)
};
とするとこれは引数に対しコピーコンストラクタが発生し更にp=_p;でも代入コピーが発生する事になります。

ところでplayer_updateの引数にPlayerは必要でしょうか?
一度Stageにメンバ変数としてPlayerをセットしておけば、引数として渡す必要は無いと思います。
    • good
    • 0
この回答へのお礼

ご指摘ありがとうございます。
やっぱり記述が間違っていました……。
ポインタはアドレスを渡して、参照渡しはコピーして渡すのではなくアドレスを介して引数をそのまま参照する、という感じでしょうか。
参照でメンバで持つ方法もあるんですね。参考にさせていただきます。

お礼日時:2009/04/07 09:23

メンバ関数として実装しようとメンバでない関数にリファレンスを渡す形にしようとメンバでない関数にポインタを渡す形にしようと, 内部的

にはほとんど変わらないんじゃないかなぁ. メンバ関数なら (隠れた引数である) this を経由してメンバ変数を参照するし, メンバでない関数の場合には引数として与えられたリファレンスやポインタを経由して参照する (リファレンスを渡す場合には結局対応するポインタを渡したのと同じ) わけだから.
    • good
    • 0
この回答へのお礼

最終的に、どれもクラス全体をコピーしているわけではないですね。
クラスの実体を渡すとクラス全部のコピーが発生して遅いというのはどこかで聞いたので気をつけていましたが……
ポインタと参照は記法も含めてまだあやふやなので今後勉強していきたいです。ありがとうございました。

お礼日時:2009/04/07 09:28

厳密なことはわからないのですが


(コンパイラの実装次第かもしれません)
参照私のほうが”ちょっとだけ"早いのではないかと思います。

理由は
”ポインタ渡し"とは厳密には"アドレスの値渡し"です。
つまり、アドレスのコピーが行われるわけです。
アドレスをコピーしても、同じオブジェクトを指すことに変わりはないため、
参照渡しと同様の効果(関数の側でオブジェクトの状態を変更して、
呼び出し側に伝えることが可能)
を得ることができます。
参照渡しなら、アドレスのコピーが行われないので早いのでは
とおもいます。

しかし、よく考えてみると
(32ビット前提で)
アドレスは4バイト、intと同じ長さです。
intのコピーにかかる時間を気にして、
intをパラメータ渡しせず
グローバル変数でアクセスさせる人はほとんどいませんよね。
4バイトのコピーなんて、ほんとに一瞬で終わるので、
気にしちゃいけません
ですから、アドレスのコピーもかかる時間は
実質無視してもかまわないと思います
    • good
    • 0
この回答へのお礼

メンバとして初めから含めておく方法はちょっとわかりませんが、
参照とポインタは速度的にも便利さの面でもほぼ対等と見てもよさそうですね。
コンパイラの実装の話まで行くとなかなか大変そうですが……
回答ありがとうございました。

お礼日時:2009/04/07 09:33

試してみました。



実行結果:
通常 7210
ポインタ渡し 4606
参照渡し 4647

何回か試しましたが、ポインタ渡しと参照渡しは、ほとんど実行速度変わんなかったです。
ちなみにintで試すと、通常が一番早くなりました。最適化すげぇ。
constとかにすると速度変わるんでしょうけど、だれか詳しい人教えてください。。


以下ソースです。
#include <stdio.h>
#include <time.h>

class test_class
{
public:
int a;
double b;
float c;
char d;
};
const int sanshou(test_class &t){
return t.a+t.b+t.c+t.d;
}
const int pointa(test_class *t){
return t->a+t->b+t->c+t->d;
}
const int nomal(test_class t){
return t.a+t.b+t.c+t.d;
}
int main(void){
test_class t;
t.a = 0;t.b = 1;t.c = 2;t.d = 3;

int prev, after;
prev = clock();
for(int i = 0; i < 100000000; i++)
nomal(t);
after = clock();
printf("%d\n", after-prev);

prev = clock();
for(int i = 0; i < 100000000; i++)
pointa(&t);
after = clock();
printf("%d\n", after-prev);

prev = clock();
for(int i = 0; i < 100000000; i++)
sanshou(t);
after = clock();
printf("%d\n", after-prev);
return 0;
}
    • good
    • 0
この回答へのお礼

回答ありがとうございました。
実際に組んで速度を比較していただいたのも嬉しいですが、
使い方の例のプログラムを載せていただいたのがかなり助かります。
こうして見ると違いがよく分かりますね……
通常の方法は、危惧していたほどの差はないけれど約1.5倍時間がかかるくらいのイメージでしょうか。
でもゲームプログラミングの性質上、メンバがほとんどintとboolなので、このままでもいいかも知れませんね。結構量があって直すのが大変そうなので、とりあえずは通常の方法のままで進めていこうと思います。

お礼日時:2009/04/07 09:40

う~ん....


参照/ポインタ渡しと「通常」って全然意味違うじゃん>#5.
あと, この手の実験は処理系が分からないと全く無意味な数値にしかならないのでそこも注意.
#5 と同じ内容の関数を constメンバ, const参照渡し, constポインタ渡しの 3つで実験してみました. 処理系は VisualStudio 2008 (Core2Duo6600, Vista Business, 4GB RAM) で, /O2 /arch:SSE2 の最適化を掛けてます.
で結論ですが.... 全く一定しません (泣) コンパイルするごとに代わる (号泣)
とはいえ, 基本的にはほぼ同じ速度です. アセンブリレベルに落ちると「const参照渡し」と「constポインタ渡し」は全く同じコードになっています. 「constメンバ」は this の渡し方の関係でちょっと違うコードになっていますが, やはり基本的には同じです.
ちなみに gcc4.1.2 (Xeon5160×2, どこかのバージョンの 64ビットな Gentoo) でやったら 3つは全く同じコードになった.
ということで, メンバ関数だろうと参照渡しだろうとポインタ渡しだろうと, とにかく「オブジェクトを丸ごとコピーする」ような真似をしない限り速度に大きな差はつかないはず. 少なくとも最初のうちはプログラムの見やすさを優先してください.
ああ, でも全体像としては「Stage のメンバとして Player を持つ」のは変な気がする. ここは設計に依存するところだし「ステージごとにプレイヤーが異なる」ならこれでもいいけど.
    • good
    • 0
この回答へのお礼

回答ありがとうございます。
参照渡しとポインタ渡しが同じコードになるというのは興味深いです。
処理系やアセンブリのあたりになるとほとんど無知なのですが
実行する環境で実行時間の結果が変わるのはなんとなく分かります。
とりあえずは自分なりに見やすいと思った方法で試してみます。

お礼日時:2009/04/08 09:04

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!