電子書籍の厳選無料作品が豊富!

こんばんは、C++のプログラミングについて質問させて頂きます。

現在、クラスを作って簡単なゲームプログラムを組んでいるのですが、メンバ関数内でクラスを宣言すると上手くオブジェクトを作成出来ないのです。

例えば、メンバ関数の外、メイン関数内で

Player Player1 = Player(320,240,100,50,32,32,3,true,"Character/Character1_A.png");
PL_Array[1]=&Player1;
FireBall FireBall1 = FireBall(PL_Array[1]->Ref_x(),PL_Array[1]->Ref_y(),PL_Array[1]->Ref_angle());
ACT_Array[1] = &FireBall1;

とすると上手く動くのですが。

このPlayerクラスPlayer1のコンストラクター内で

FireBall FireBall1 = FireBall(Ref_x(),Ref_y(),Ref_angle());
ACT_Array[1] = &FireBall1;

とすると、FireBall1オブジェクトが生成されないのです。

この状態を改善するためにはどうすればいいのでしょうか、そもそもクラス内で他のクラスを作成する、といった動作自体があまり良く無いことなのでしょうか・・・

お時間がありましたら、お答え頂けると助かります、宜しくお願いします。

A 回答 (3件)

生成はされています。


ただし

FireBall FireBall1 = FireBall(Ref_x(),Ref_y(),Ref_angle());



FireBall FireBall1( Ref_x(),Ref_y(),Ref_angle() );

といった書き方だと、これは「ローカル変数」という扱いになります。
ローカル変数は関数をはじめとする、スコープを抜けるときに寿命が終わりますので
コンストラクタが終わってmain関数の方の処理に戻ったら、もう破棄されているというわけです。

main関数内(かつ、特別にスコープを与えない)で作ればmain関数が終わるまでは使える、ということになります。


>クラス内で他のクラスを作成する、といった動作自体があまり良く無いことなのでしょうか・・・


いえいえ、むしろC++ならしょっちゅう行いたいことですよ。
クラスはメンバ関数だけでなくメンバ変数を持てることはご存知ですよね?




class BをB.hに作っておくとします。

つぎにclass Aを作るときに

/* Bの宣言部がAから見えるようにインクルードします。*/
#include "B.h"

class A {

B b; //小文字のbの部分は好きな名前

public:

A();
~A();

void f() const;

};


これだけでOKです。
これだとAのコンストラクタで作られ
デストラクタで破棄されます。
Bのコンストラクタが引数を持つ場合は

A::A() : b(引数のリスト) {
}

などと初期化子リストで明示的に書いてやる必要がありますが
もし引数なしなら

A::A(){}

と、何も書かなくても大丈夫です。
デストラクタではどちらでも何も書かなくて大丈夫です。

使うときは

void A::f() const {
b.Bのメンバ関数名();
}

等で使えます。(この場合だとBのconstメンバ関数しか使えませんが)


ただこの調子で沢山クラスを作ってやってくと
#includeが増えまくってしまう
という恐れがあります。
これはコンパイル時間の増大や

BがAのインスタンスを持ち
AがBのインスタンスを持ちたい

などといった場合に行き詰るはめになります。
なので、一般的にはこういう方法が良いです。

class B; //Bというクラスが存在するよ、という宣言

class A {

/* ポインタや参照で持つ場合は、この時点でBの中身は分からなくていいので、インクルードする必要がありません。*/

B* b;

public:

A();
~A();

void f() const;

};

そしてソースの方ではB.hをインクルードします。

そして例えばこのように書きます。

A::A() : b(NULL) {
b = new B(引数のリスト); //new演算子で動的に確保します
}

//動的確保をしたら同じ回数だけ、対になるようにdeleteで解放してください。

A::~A(){
delete b;
}


使うときはポインタなのでこうですね。

void A::f() const { b->Bのメンバ関数名(); }

(この場合だとBの、constでないメンバ関数も使えます)


動的確保、解放の場合は
コンストラクタ・デストラクタ以外でも自由なタイミングで確保・解放が出来ます。
ただし、ちゃんと管理しないと
メモリリークやアクセス違反が発生する恐れがあるのでご注意ください。

delete NULL;

は「何もしない」ということに決まっているので
delete したらすぐNULLを代入

delete b; b = NULL;

などを徹底しておけば基本的に大丈夫だと思いますが

一般的には「スマートポインタ」という、その辺の管理をほとんど自動的に行ってくれるような手法が使われたりします。



ただ、今回どういう用途なのか分かりませんが
メンバとして

class B;

class A {
B* b;
public:
void f() const;
};

と持たせるより
外部で作っておいて

class B;

class A {
public:
void f(B* b) const;
};

などとしておいて
Bを使いたい時だけ

void A::f( B* b ) const {
b->Bのメンバ関数名();
}


などとした方が分かりやすい場合もあります。
それは設計次第です。

AとBが密接にかかわるクラスだったら
メンバに持たせるのが筋なことが多いですが


たまーにしか関わらないのであれば
特定の関数で受け渡したりしてそこでだけ使った方が良い場合もまた、多いです。
    • good
    • 0

#1さん


>今の VC++ はちゃんと bad_alloc 投げるんだっけ?>new 失敗時

すべてのVC++のバージョンが規格に対してそれぞれどうなってるかは知りませんが
C++の言語仕様では明示的に書かない限り
コンストラクタでの例外は
「投げられた例外」が呼び出し側に次々に伝搬することになってたかと

struct AAAA { AAAA(){ throw 1000; } };
struct BBBB { AAAA a; };

try { BBBB b; }
catch ( int i ){
printf( "%d", i );
getchar();
}

なのでstd::bad_allocとは限りませんが
仮にstd::bad_allocが投げられれば、特に対処していない場合一番根本の呼び出し元まで例外が飛んでいくはずです。
    • good
    • 0

オブジェクトの生成自体はできているはずです。

ただ、関数(コンストラクタを含む)を抜けた時点で破壊されているだけです。

これをやるなら
FireBall* FireBall1 = new FireBall(Ref_x(),Ref_y(),Ref_angle());
ACT_Array[1] = FireBall1;
と書きましょう。ただし、ちゃんと生成されたか確認することと、デストラクタまたは適切なタイミングで delete することを忘れずに。
# えーと、今の VC++ はちゃんと bad_alloc 投げるんだっけ?>new 失敗時
    • good
    • 0

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