構造体の前方宣言

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Rittai_3D
記事: 525
登録日時: 12年前

構造体の前方宣言

#1

投稿記事 by Rittai_3D » 11年前

こんにちは、3Dです。今回は構造体の前方宣言について質問します。
敵移動プログラムを書いている時に、訳が分からなくなりました。
► スポイラーを表示
このようなコードを書いてみました。Enemy_MovePat.cpp/hのEnemy_Move001をEnemy.cpp内に書いた場合は思い通りに動きますが、ファイル分割を
すると
► スポイラーを表示
のようなエラーが出ます。
また、このエラーを消そうと以下のコードを書き直しました。
► スポイラーを表示
これをコンパイルすると
► スポイラーを表示
とエラーが出ます。どの様にすればコンパイルが通るでしょうか?
【開発環境】
Visual C++ 2008 Express Edition
C言語
Dxライブラリ
初心者です

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 構造体の前方宣言

#2

投稿記事 by みけCAT » 11年前

「とりあえず」、最初のEnemy_MovePat.h (Enemy_MovePat.cppの間違い?)の4行目(#include "Enemy.h"の次の行)に、
Enemy.cppの7~15行目の構造体宣言をコピペすればコンパイルは通ると思います。
ただし、これはメンテナンス性を下げ、いい方法ではありません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: 構造体の前方宣言

#3

投稿記事 by softya(ソフト屋) » 11年前

Enemy.cppしか知らないはずの構造体のメンバ情報にEnemy_MovePat.cppがアクセスしているのでエラーになります。
カプセル化としてEnemy.cppに閉じ込めているはずのものなので、Enemy_MovePat.cppが参照できては行けないはずのものです。
なので設計がおかしいと言うことです。

※ これってつまり、カプセル化の意図や機能、効用を理解できていないんじゃないかと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Rittai_3D
記事: 525
登録日時: 12年前

Re: 構造体の前方宣言

#4

投稿記事 by Rittai_3D » 11年前

返信ありがとうございます。
「とりあえず」、最初のEnemy_MovePat.h (Enemy_MovePat.cppの間違い?)の4行目(#include "Enemy.h"の次の行)に、
Enemy.cppの7~15行目の構造体宣言をコピペすればコンパイルは通ると思います。
ただし、これはメンテナンス性を下げ、いい方法ではありません。
申し訳ありません。ご指摘ありがとうございます。間違えておりました。修正いたします。
ヘッダファイルに構造体を書いて公開してしまう方法以外で方法はないでしょうか?
Enemy.cppしか知らないはずの構造体のメンバ情報にEnemy_MovePat.cppがアクセスしているのでエラーになります。
カプセル化としてEnemy.cppに閉じ込めているはずのものなので、Enemy_MovePat.cppが参照できては行けないはずのものです。
なので設計がおかしいと言うことです。

※ これってつまり、カプセル化の意図や機能、効用を理解できていないんじゃないかと思います。
そういう意味だったのですか。エラーの理由が分かりました。エラーの理由が意味不明でしたので...。
方法をいくつか考えてみます。もしよければヒントをください。
オフトピック
error C2065: 'nCnt'; : 定義されていない識別子です。
「いやいやいや書いてあるじゃん。ちゃんと自動で補完してくれるちっこいウィンドウ出てくるし」
という意味です。
初心者です

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: 構造体の前方宣言

#5

投稿記事 by みけCAT » 11年前

3D_3D さんが書きました:ヘッダファイルに構造体を書いて公開してしまう方法以外で方法はないでしょうか?
・Enemy_tの定義を別のヘッダファイルに書き、それぞれのソースでインクルードする
・新しいヘッダを使わず、enemy.cppに#ifdefなどでEnemy_MovePat.cppから直接インクルードできる仕組みを作る
という方法が考えられます。
ヘッダファイルに構造体を書きたくないのでしたら、後者の方法がいいのではないでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: 構造体の前方宣言

#6

投稿記事 by softya(ソフト屋) » 11年前

enemy.cppにカウンタを参照する関数や、ベクトルを引数にして移動させる関数などを用意するのも手ですね。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: 構造体の前方宣言

#7

投稿記事 by usao » 11年前

うーん,なぜヘッダを作るのを嫌うのでしょう?
現在Enemy.cppに書かれているstruct Enemy_tを新しいヘッダ(Enemy_t.h)に移し,
Enemy.cppと,移動パターン関数を実装しているファイル(Enemy_MovePat.hって書いてあるけど.cppかな?)とで
includeする形が最も素直な気がしますが…
(Enemy.hに書かれている struct Enemy_t; は不要ですね.)

この場合,「新しい Enemy_t.h は,Enemy.cppとEnemy_MovePat.cpp だけが使うもの」であると考えれば
それ以外の外部には Enemy_t の詳細は(というかこの場合だと,その存在すら)隠されていますよね.
仮にmain.cppとかでうっかり Enemy_t.h をincludeした場合,struct Enemy_t型に関する情報は知られてしまいますが,
それを知ったところで Enemy.cpp 内にある変数 gEnemy[] にアクセスできるわけではないので問題にはなりませんよね.

(まぁ,これだとタイトルの「前方宣言」とは関係ない形になってしまいますが…)
[hr]
オフトピック
私なら

コード:

//Enemy.h
struct Enemy_t;

Enemy_t *CreateNewEnemyInstance();  //新しいEnemy_t型オブジェクトが作成され,そのポインタが返される.
void DeleteEnemyInstance( Enemy_t *pEnemyInst );  //CreateNewEnemyInstance()で作られたオブジェクトの破棄用.

void Enemy_Init( Enemy_t *pEnemyInst );  //引数で指定されたEnemy_tオブジェクトに関して,初期化処理を行う
int Enemy_Updata( Enemy_t *pEnemyInst );  //引数で指定されたEnemy_tオブジェクトに関して,初期化処理を行う
...
みたいな形でやるかも.
例えばmain側では,
CreateNewEnemyInstance()を使って新しい敵を作ってもらい,
それをEnemy_Init()とかに渡すことで仕事してもらい,
いらなくなったらDeleteEnemyInstance()で捨ててもらう.

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: 構造体の前方宣言

#8

投稿記事 by softya(ソフト屋) » 11年前

> うーん,なぜヘッダを作るのを嫌うのでしょう?

C++のカプセル化を目指してC言語で実装していると思っているんですが。
privateをC言語で実現するための手法としては、構造体のメンバを他に明かさないのが私は最良かと思います。

【補足】
最終的に決めるのは3D_3Dさんですので、3D_3Dさんの判断にお任せします。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: 構造体の前方宣言

#9

投稿記事 by usao » 11年前

>C++のカプセル化を目指してC言語で実装していると思っているんですが。

main側とかに明かしたくない,というレベルの話ではなくて
Enemy_MovePat.cppにすら構造体の詳細を明かさない ことを目指しているという話ですね.
勘違いしていたようです.

Rittai_3D
記事: 525
登録日時: 12年前

Re: 構造体の前方宣言

#10

投稿記事 by Rittai_3D » 11年前

みなさん、返信ありがとうございます。

>みけCATさん
・Enemy_tの定義を別のヘッダファイルに書き、それぞれのソースでインクルードする
・新しいヘッダを使わず、enemy.cppに#ifdefなどでEnemy_MovePat.cppから直接インクルードできる仕組みを作る
という方法が考えられます。
ヘッダファイルに構造体を書きたくないのでしたら、後者の方法がいいのではないでしょうか?
#ifdefを使うのですか。全く思いつきませでした。
プリプロセッサの使い道がインクルードガードくらいしかない物だと思っていました。
プリプロセッサをとりあえず使ってやってみます。

>softyaさん
enemy.cppにカウンタを参照する関数や、ベクトルを引数にして移動させる関数などを用意するのも手ですね。
カウンタを参照とは

コード:

int getCount()
{ return g_nCnt; }
のようにする事ですか?

ベクトルを使用して移動させるコードを書いてみましたが、よく分かりません。ベクトルの理解が危ういので本当にこうでよいのかすら
分かりません。
*コードは一番下の物です

>usaoさん
うーん,なぜヘッダを作るのを嫌うのでしょう?
現在Enemy.cppに書かれているstruct Enemy_tを新しいヘッダ(Enemy_t.h)に移し,
Enemy.cppと,移動パターン関数を実装しているファイル(Enemy_MovePat.hって書いてあるけど.cppかな?)とで
includeする形が最も素直な気がしますが…
ヘッダファイルは嫌っていません。
softyaさんの返信の
C++のカプセル化を目指してC言語で実装していると思っているんですが。
privateをC言語で実現するための手法としては、構造体のメンバを他に明かさないのが私は最良かと思います。
これが目標ですので、出来る限り変数は隠蔽したいのです。
わざわざサンプルコードまで書いて頂いたのに申し訳ないです。

------------------------------------------------
以下のコードは自分がこんな感じかなと思い書いてみたコードになります。
ベクトルがいまいち理解できていないのでこうした方が良いなどのアドバイスをくださるとうれしいです。
修正したあるいは追加した部分のコードだけ載せます。
► スポイラーを表示
初心者です

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: 構造体の前方宣言

#11

投稿記事 by softya(ソフト屋) » 11年前

私の提案は逆だったのです。
Enemy_Move001はEnemy_Move(Enemy_t* pEnemy,vet_t vec);を呼び出す形です。
移動ベクトルは移動距離ですので、今+=5.0とかしている値を渡します。

ただ、これだと
Enemy→Enemy_Move001→Enemy
と出て入る形が気持ち悪いので(感覚的に分かりづらい)ので、 
EnemyMng→Enemy_Move001→Enemy 動くなど
EnemyMng→Enemy 描画など
とした方が良いかもしれません。
この場合、Enemyポインタ配列はEnemyMngが管理しますので、Enemy_Action()とEnemy_Enter()もEnemyMngに移ります。

あるいは、3D_3Dさんの考えでいくならEnemy→Enemy_Move001の場合、ベクトルじゃなく座標を渡します。

コード:

// Enemy.h
typedef struct Point_t
{
    double      x, y;
}

// Enemy.cpp
void ( *EnemyMovePat[] )( Point_t*pPoint, int cnt) =
{
    Enemy_Move001,
};

// Enemy_Move001.cpp
void Enemy_Move001( Point_t* pPoint, int t )
{
    if( t < 120 ) {
        pPoint->y += 5.0;
    }
 
    if( t < 120 + 120 ) {
        pPoint->y += 0.0;
    }
 
    if( t < 120 + 120 + 120 ) {
        pPoint->y -= 5.0;
    }
}
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Rittai_3D
記事: 525
登録日時: 12年前

Re: 構造体の前方宣言

#12

投稿記事 by Rittai_3D » 11年前

返信ありがとうございます。

>softyaさん
私の提案は逆だったのです。
Enemy_Move001はEnemy_Move(Enemy_t* pEnemy,vet_t vec);を呼び出す形です。
移動ベクトルは移動距離ですので、今+=5.0とかしている値を渡します。

ただ、これだと
Enemy→Enemy_Move001→Enemy
と出て入る形が気持ち悪いので(感覚的に分かりづらい)ので、 
EnemyMng→Enemy_Move001→Enemy 動くなど
EnemyMng→Enemy 描画など
とした方が良いかもしれません。
この場合、Enemyポインタ配列はEnemyMngが管理しますので、Enemy_Action()とEnemy_Enter()もEnemyMngに移ります
何か勘違いをしていたようです。なるほど、そういう事でしたか。
ベクトルはあきらめました。すいません。
あるいは、3D_3Dさんの考えでいくならEnemy→Enemy_Move001の場合、ベクトルじゃなく座標を渡します。

コード:

// Enemy.h
typedef struct Point_t
{
double x, y;
}

// Enemy.cpp
void ( *EnemyMovePat[] )( Point_t*pPoint, int cnt) =
{
Enemy_Move001,
};

// Enemy_Move001.cpp
void Enemy_Move001( Point_t* pPoint, int t )
{
if( t < 120 ) {
pPoint->y += 5.0;
}

if( t < 120 + 120 ) {
pPoint->y += 0.0;
}

if( t < 120 + 120 + 120 ) {
pPoint->y -= 5.0;
}
}
そのやり方を実装した所、自分の思っている動作をしました。個人的にこっちの方が分かりやすい気がします。
それにベクトル苦手なので...

なにはともあれ、完成して良かったです。ここをこうした方が良いよなどのアドバイスがあればお教え下さい。
コードが汚いのはスルーしてください。
softyaさん、みけCATさん、usaoさん、ありがとうございました。
► スポイラーを表示
初心者です

閉鎖

“C言語何でも質問掲示板” へ戻る