プロが教える店舗&オフィスのセキュリティ対策術

こんばんは、C++のプログラミングに関しての質問です。

現在、勉強がてら作っているゲームプログラミングで、躓いている部分があります。

現在、プレイヤーではないオブジェクトの描画を以下のソースコードで描画しています。

for(Check_OBJ=1;Check_OBJ<OBJ_num;Check_OBJ++)
OBJ_Array[Check_OBJ]->Draw_Graph(PL_Array[1]->Ref_x(),PL_Array[1]->Ref_y());

OBJ_ArrayはObjectクラス、PL_ArrayはPlayerクラス、Ref_変数名 は変数名の値を返す関数です。

このソースコードを、Objectクラスのメンバ関数Action()の中に入れたいのですが、エラーが出てしまうのです、ソースコードは以下の通りです。

void Object::Action()
{
Draw_Graph(PL_Array[1]->Ref_x(),PL_Array[1]->Ref_y());
}

エラー内容は

error C2227: '->Ref_y' : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。
error C2027: 認識できない型 'Player' が使われています。

です。

Playerクラスが定義されてないと認識しているのかと思い、前方宣言もしてみたのですが改善できず、どうすれば良いのか解りません。

クラス内で他のクラスへアクセスするためには何か他の処理が必要なのでしょうか・・・?
解決方法をご存知の方がいたら教えて頂けると助かります、よろしくお願いします。

A 回答 (2件)

>Objectクラスのメンバである必要があります。



あ、一応PL_Arrayがもしグローバルだったらそうでなくても出来ますねw
(よほどいろんな場所から使われたりするとか、特別な事情がない限りお勧めはできませんが)


※以下はC++の仕様についての補足説明です。


なお、「宣言」と「定義」というのは言葉の上では事実上曖昧になる場合もありますし、同時に行われる場合もありますが、基本的には別々と考えてください。

クラスの場合、(前方)宣言というのはあくまで

class A;

Aの内容は知らないけどAというものがある
ということしか示しません。
Aのメンバを使いたかったら

class A {
public:
void f();
};

こういう風に書かれたものが、使いたい場所より前に書いてないといけません。
通常
#include
によって、前に書かれたのと同じことにしているわけです。

で、さらに言うとこのときの

void f();

は「宣言」です。
これは、引数なしで戻り値なしのfという関数をAはメンバとして持ってる。

という事を示していますが
(ホントはconstじゃないthiscallのメンバ関数、とか、これだけでももうちょい色々情報があるのですが)

少なくとも関数の定義(実装)の事はこの部分だけでは全く分かりません。

全く分からなくても、これだけの情報があればfという関数を呼び出すには十分なのです。

もちろん、宣言だけして
どこにも実装(関数定義)を書かなければ
コンパイルは通るものの「リンクエラー」が発生してビルドできません。

なので、どこかに書いてやる必要はあります。
ただし、複数書いてもやはりいけません。

全く同じ関数の「定義部分」を複数書くと、どっちの定義を使えばいいかコンパイラが判断できなくなりますから
関数定義を書けるのは一か所だけです。

どこに書くかは

class A {
public:
void f();
};

が見える位置で、他の場所に何度もインクルードされない、どっかのソースに書く、というのが一般的になります。(ヘッダに書いたりすると何度もインクルードされて、「何度も書いた」事と同じになってしまう可能性がある。)

ただし

class A {
public:
inline void f();
};

などとした場合(やtemplateにした場合など)は
インライン化するためには関数の定義が見えないといけません。
(実際にインライン展開されるかどうかは別として)

なので、この場合

f関数を使用する前には
f関数の内容が見えてないといけません。


ただしインライン関数の場合は
ヘッダに書いて何度も別のところでそのヘッダをインクルードしても
大丈夫ということになっています。

なので

class A {
public:
inline void f();
};

inline void A::f(){}

こういう風にヘッダに書いておくことが可能です。
もっというと

class A {
public:
inline void f(){}
};

このように宣言と定義を同時に書くこともできます。
さらに言えば

class A {
public:
void f(){}
};

class や struct内では、このようにinlineを書かずに
関数の宣言と定義を同時に行う事もできます。

この場合inlineが書かれていませんが
「書かれているのと同じ意味」として扱われます。

巨大な関数で、かついろんな場所から呼び出される場合はやらない方がいい可能性が高いですが

ささいなSetterやGetterではこのようにするのが大抵良いです。

class A {

int num;

public:

void SetNum(int i){ num = i; }
int GetNum() const { return num; }

};
    • good
    • 0
この回答へのお礼

下記の解答を見て、問題が解決してお礼を書き込んでいる最中にこちらの解答に気が付きました。

お察しの通りPL_Arrayはグローバルに宣言していました、セキュリティの面で問題があるようですので、後ほど見直して見たいと思います。


先程の解答を見た後、試行錯誤して、#inluce "Player.h"をアクション関数の上に置くか、アクション関数をPlayer.hの中に持ってくる、等の方法で解決することが出来ました、ありがとうございます。


その後、この解答を見てインライン関数について自分でも少し調べてみたのですが、インライン関数とは関数を呼び出すコードの中に直接定義を呼び出し実行する、ということ・・・ですよね?
ちょっとinline関数の方は見て直ぐに理解するのは出来なかったので、少々勉強してみることにします、ありがとうございます。

お礼日時:2012/02/18 04:10

前回質問で私は一辺に色々と書いたので、C++に触れ初めて間もない状態では、一発で隅々まで吸収するのはさすがに難しいと思いますので



確認していきましょう。


void Object::Action()
{
Draw_Graph(PL_Array[1]->Ref_x(),PL_Array[1]->Ref_y());
}


まず、この書き方だと

PL_ArrayはPlayerクラスのポインタのポインタ
あるいはポインタの配列になっていて、同時にObjectクラスのメンバである必要があります。


なのでObjectクラスのヘッダ(宣言)部分で、まず、こういった形になっていますか?


class Player;

class Object {

Player** PL_Array; //あるいはPlayer* PL_Array[定数];

戻り値 Draw_Graph(引数のリスト);

public:

void Action();

};


つぎに

#include "Playerクラスの内容が書いてあるヘッダ名.h"


void Object::Action()
{
Draw_Graph(PL_Array[1]->Ref_x(),PL_Array[1]->Ref_y());
}

というように
Action関数の上にヘッダのインクルードの記述がありますか?

(これ以外の事は通常では考えにくいので、これらの両方を満たしていてなおかつこうなるという場合は、ヘッダとソースのコードを正確に載せてみてください。)
    • good
    • 0
この回答へのお礼

解答ありがとうございます。

教えて頂いた2つの点についてソースコードを確認し、修正してみたところ、Action()の上にPlayerクラスをインクルードしていなかったことが原因のようでした。

そこを修正すると、無事にエラーが出なくなりました、有難うございます。

お礼日時:2012/02/18 03:47

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

このQ&Aを見た人はこんなQ&Aも見ています


人気Q&Aランキング