すでに、もう、1年ほど前ぐらいに解決してしまった掲示板の
http://isweb6.infoseek.co.jp/computer/t-soga/cgi-bin/wwwlng.cgi?print+200102/01020031.txt
なんですが、これにだいぶ近い質問です。
同じように、私も別のクラスからCxxxDocクラスのポインタを取得したいのですがなかなかうまく行きません。
CxxxDoc *pDoc = (CxxxDoc*)((CFrameWnd *)AfxGetMainWnd())->GetActiveDocument();
も試してみたんですが、ビルドすると「エラー 0、警告 0」となって一見うまく行くんですが、プログラムを
実行すると、Debug Assertion Failed!になってしまいます。このコードを書いた場所が悪いのかと思ったん
ですが、何か、このコードを使える制限とかあるのでしょうか。よろしくお願いします。
それと、重さんがやるように、CxxxDocクラスで渡したいクラスのメンバ関数の引数としてthisポインタを
設定するという方法も試しているんですが、こちらもうまく行きません。どうやら、ヘッダーファイルの
インクルードに原因がありそうだと思うんですが原因がわからないです。
そこで、いくら読んでもなかなかわからないんですが、重さんがいう、
>> CSanmokuDoc.hでCSanmokuDlg.hをインクルードする場合には少々細工が必要になると思われます。
>1.細工と言ってもマクロでOK。
>2.CSanmokuDoc.hでインクルードせずに.cppでインクルードすれば何も問題なし。(グローバルのみでしか
>扱えませんが)
はどういう意味なんでしょうか。ここでいう「マクロ」とは同じ操作の繰り返しを記録する「クイックマクロ機能」
のことでしょうか。それとも、#defineで定義するマクロのようなことでしょうか。
わからないことだらけで申し訳ありません。もう、かれこれ2日ほどここでとまっているので、とても困ってます。
どんなことでも結構です。重さん、たかしさん以外の人でも、何かヒントになりそうなこと、気づいたことが
あったら教えてください。よろしくお願いします。
環境は、Win98SE, VC++6.0です。
> ここでいう「マクロ」とは同じ操作の繰り返しを記録する「クイックマクロ機能」
> のことでしょうか。それとも、#defineで定義するマクロのようなことでしょうか。
#defineです。
> CSanmokuDoc.hでCSanmokuDlg.hをインクルードする場合には少々細工が必要になると思われます。
CSanmokuDlgクラスで CSanmokuDocを使う為に CSanmokuDlg.hでは CSanmokuDoc.hをインクルードする。
で、CSanmokuDocクラスで CSanmokuDlgクラスを使う為に CSanmokuDlg.hをインクルードする。
この場合、CSanmokuDoc.hで CSanmokuDlg.h、さらに CSanmokuDlg.hで CSanmokuDoc.hと互いにインクルードを
しているので、そのままだと2重定義でコンパイルが通らないと思います。
その為に、マクロで2重定義を回避するか、CSanmokuDlg.cppで CSanmokuDoc.hをインクルードすれば
2重定義を回避できます。
順序が逆になりましたが・・・
> 同じように、私も別のクラスからCxxxDocクラスのポインタを取得したいのですがなかなかうまく行きません。
> CxxxDoc *pDoc = (CxxxDoc*)((CFrameWnd *)AfxGetMainWnd())->GetActiveDocument();
クラスのメンバとして使用する場合にはコンストラクタで良いのではないでしょうか?
> 実行すると、Debug Assertion Failed!になってしまいます。
これは、CxxxDoc *pDoc = (CxxxDoc*)(・・・・・ の宣言で発生しているのでしょうか?
助かりました。重さん。もう、だいぶ前の話なので、半分あきらめてたんですが、これで何とかできそうな気がしてきました。早速、いろいろ試してみます。
はじめ、重さんのいう「2重定義」の意味がわからなかったんですが、それぞれのクラスごとが何を#includeしてる
のか全部書き出してみたら、だいぶわかってきました。重さんの言った通り、確かに、2重定義が原因でエラーが
出てるようでした。
少しわかりにくいかもしれませんが、私の場合
ドキュメントクラス → CInduDocクラス
ドキュメントクラスのポインタを取得したい別のクラス → CEsaクラス
ドキュメントクラスのポインタを使いたい関数 → CEsa::DrawEsa(CDC *pDC, CInduDoc* pDoc)
と言う風になってます。たかしさんのと比べると
たかしさん 私 ソースファイル ヘッダーファイル
CSanmokuDocクラス → CInduDocクラス → InduDoc.cpp → InduDoc.h
CSanmokuDlgクラス → CEsaクラス → Esa.cpp → Esa.h
という風に対応しています。
初めに試したのが、重さんのいう
>CSanmokuDlg.cppで CSanmokuDoc.hをインクルードすれば2重定義を回避できます。
(私の場合だと、CEsa.cppでCInduDoc.hをインクルードするということ)
です。これは上手な方法だなあって思ってやってみたんですが、エラーがでてしまいます。エラー内容は、Esa.hで
構文エラー : 識別子 'CInduDoc' がシンタックスエラーを起こしました。
と言う風になってしまいます。間接的にでもEsa.hをインクルードしたソースファイルではそれぞれのコンパイル時に
そうなってしまうようです。
重さんのいう、
>(グローバルのみでしか扱えませんが)
とはこのことを意味しているのでしょうか。
もう一つの方法のマクロの方も試してみようと思って、マクロで2重定義を回避する方法を書籍の中で探してるんです
が見つかりません。どういうあたりに書いてあるとかでもいいので、教えていただけないでしょうか。
>> 実行すると、Debug Assertion Failed!になってしまいます。
>これは、CxxxDoc *pDoc = (CxxxDoc*)(・・・・・ の宣言で発生しているのでしょうか?
私は
void CEsa::DrawEsa(CDC *pDC)
{
CInduDoc *pDoc = (CInduDoc *)((CView *)AfxGetMainWnd())->GetDocument();
・
・
・
if(ryou[i][j] == pDoc->GetPreyEnergy()) //ここで止まってしまう
という感じで、ドキュメントクラス用にメンバ変数を宣言せずに、メンバ関数の中でポインタを取得するように
しました。デバッグで調べてる限りだと上で示したところのところで止まってしまうようです。止まる直前のpDocの
値が0x00000001なのでpDocに何も入ってないのが原因のような気がします。
説明がすこしごちゃごちゃして、わかりにくくなってしまったかもしまいませんが、よろしくお願いします。
> これは上手な方法だなあって思ってやってみたんですが、エラーがでてしまいます。エラー内容は、Esa.hで
> 構文エラー : 識別子 'CInduDoc' がシンタックスエラーを起こしました。
Esa.hで CInduDocを宣言していませんか?
CEsa.cppで CInduDoc.hをインクルードするのなら、CEsa.hで CInduDocクラスを扱うことは出来ません。
>重さんのいう、
>(グローバルのみでしか扱えませんが)
> とはこのことを意味しているのでしょうか。
その通りです。
> もう一つの方法のマクロの方も試してみようと思って、マクロで2重定義を回避する方法を書籍の中で探してるんです
> が見つかりません。どういうあたりに書いてあるとかでもいいので、教えていただけないでしょうか。
私もマクロを扱った書籍は見たことがありません。
身近な例ではMFCのヘッダーを読むのがよいかと思います。
> void CEsa::DrawEsa(CDC *pDC)
> {
> CInduDoc *pDoc = (CInduDoc *)((CView *)AfxGetMainWnd())->GetDocument();
> ・
> if(ryou[i][j] == pDoc->GetPreyEnergy()) //ここで止まってしまう
CInduDoc *pDoc = (CInduDoc *)((CView *)AfxGetMainWnd())->GetDocument();
この部分、CView * ではなく、CFrameWnd * では?
重さん、できました!うれしいです!3日目にしてやっと前進しそうです。
重さんの言った通り、
CInduDoc *pDoc = (CInduDoc *)((CFrameWnd *)AfxGetMainWnd())->GetActiveDocument();
と変えれば無事動きました。でも、どうして、
CInduDoc *pDoc = (CInduDoc *)((CView *)AfxGetMainWnd())->GetDocument();
ではダメだったかが不思議です。このソースファイルEsa.cppは
#include stdafx.h
#include Indu.h
#include InduDoc.h
#include Esa.h
の4つのヘッダーファイルをインクルードしています。「CView *」ではダメで、「CFrameWnd *」でいい訳が
#include Indu.hあたりにあるのでしょうか。不思議です。
>私もマクロを扱った書籍は見たことがありません。
>身近な例ではMFCのヘッダーを読むのがよいかと思います。
結構、難しそうな話なんですね。どおりで本になかなかでてないはずです。早速、MFCをチェックしてみます。
どちらにしろ、これで何とかやっていけそうです。今回は、とてもいい勉強になりました。長々とした質問に
丁寧に答えていただいてありがとうございました。月並みな言葉ですが、ありがとうございました。
問題解決されたようですが、
> CInduDoc *pDoc = (CInduDoc *)((CView *)AfxGetMainWnd())->GetDocument();
> ではダメだったかが不思議です。
この場合(CView *)とキャストしGetDocument()を呼んでいますが、CViewがどのビューを指し、
どのドキュメントに関連付けられているか全く判らないです。
ですから、このGetDocument()はNULLを返してきたはずです。
すみません、やっぱり、問題解決してませんでした。実は、同じことを他のメンバ関数で試すと、また、うまく
行かなくなってしまいました。
void CEsa::InitCEsa()
{
CInduDoc *pDoc = (CInduDoc *)((CFrameWnd *)AfxGetMainWnd())->GetActiveDocument();
デバッグしてる限りだと、どうやら、AfxGetMainWnd()の戻り値が0x0000000{CWnd hWnd=???}となってうまく
いってないような気がします。いっそのこと、それぞれのメンバ関数で内部的に宣言せずに、メンバ変数として
宣言してコンストラクタで取得しようかと思ったのですが、それだと結局、
class CEsa
{
public:
void InitCEsa();
void DrawEsa(CDC *pDC);
CEsa();
virtual ~CEsa();
private:
CInduDoc* m_pDoc; //メンバ変数として宣言
}
//コンストラクタ
CEsa::CEsa()
{
m_pDoc = (CInduDoc*)((CFrameWnd *)AfxGetMainWnd())->GetActiveDocument();
}
となって、#include InduDoc.hが必要になってしまうと思います。
本を読んでいると、ドキュメント/ビューアーキテクチャの話がよく出てきて、ドキュメントクラスでデータを管理
するとあったので、つい、ドキュメントクラスでさまざまな変数を宣言して、他のクラスから参照するようにして
いたのですが、このようにドキュメントクラスで変数を宣言せずに、他のクラスでデータを宣言して、そのデータを
ドキュメントクラス側から取得するといった方がいいのでしょうか。オブジェクト指向の考え方がまだまだわからず、
とんでもないことを質問してるかもしれませんが、なにかヒントでもいいのでお気づきな点があったら教えてくださ
い。
それと、
>> CInduDoc *pDoc = (CInduDoc *)((CView *)AfxGetMainWnd())->GetDocument();
>> ではダメだったかが不思議です。
>この場合(CView *)とキャストしGetDocument()を呼んでいますが、CViewがどのビューを指し、
>どのドキュメントに関連付けられているか全く判らないです。
ということは、CFrameWndだと関連付けがわかるということでしょうか。初めは、Indu.hのどこかで、CFrameWndとド
キュメントを関連付けているのかと思ったのですが、もっと、内部の方で行っているのでしょうか。こちらもよろしく
お願いします。
> CInduDoc *pDoc = (CInduDoc *)((CFrameWnd *)AfxGetMainWnd())->GetActiveDocument();
> デバッグしてる限りだと、どうやら、AfxGetMainWnd()の戻り値が0x0000000{CWnd hWnd=???}となって
この時点で、pDocはどうなっていますか?
一見では、おかしくはないように思えます。
メンバとして扱う場合には、当然ヘッダーで #include InduDoc.hが必要になります。
何もせずに記述すると2重定義となる為、マクロで回避する必要があります。
> 本を読んでいると、ドキュメント/ビューアーキテクチャの話がよく出てきて、ドキュメントクラスでデータを管理
> するとあったので、つい、ドキュメントクラスでさまざまな変数を宣言して、他のクラスから参照するようにして
ドキュメントクラスで変数を宣言する方法で良いと思いますよ。
Viewと関連づけがされているのでViewに絡む部分の扱いが容易ですから。
> ということは、CFrameWndだと関連付けがわかるということでしょうか。初めは、Indu.hのどこかで、CFrameWndと
CViewの場合は GetDocument、CFrameWndではGetActiveDocument、この違いはMSDNを見てみれば判ると思います。
一度MSDNを見てみて、それでも判らない場合は再度書き込みして下さい。
私の判る限りですがお答えします。
いつも丁寧な説明、申し訳なく思えます。早速、いろいろ調べてみます。
>この時点で、pDocはどうなっていますか?
>一見では、おかしくはないように思えます。
void CEsa::InitCEsa()
{
● CInduDoc *pDoc = (CInduDoc *)((CFrameWnd *)AfxGetMainWnd())->GetActiveDocument();
上の場所にブレークポイントを挿入して実行して止まったときは、変数ウィンドウは
シンボル名 値
pDoc 0xcccccccc{CInduDoc}
this 0x0067e22c
となってます。ここからステップインさせると、画面が移って
_AFXWIN_INLINE CWnd* AFXAPI AfxGetMainWnd()
{ CWinThread* pThread = AfxGetThread();
return pThread != NULL ? pThread->GetMainWnd() : NULL; }
とAfxGetMainWnd()に飛んでるみたいなんですが、ここでステップアウトすると、
void CEsa::InitCEsa()
{
● CInduDoc *pDoc = (CInduDoc *)((CFrameWnd *)AfxGetMainWnd())->GetActiveDocument();
と再び、ここに戻ってきて、そのときの変数ウィンドウは下のようになってます。
シンボル名 値
pDoc 0xcccccccc{CInduDoc}
this 0x0067e22c
AfxGetMainWnd戻り値 0x00000000{CWnd hWnd=???}
この後、またステップインすると
「ハンドルされていない例外はIndu.exeにあります:0x00000005:Access Violation」
というメッセージウィンドウが出てきます。
これは、アクティブなメイン ウィンドウへのポインタを取得できてないってことでしょうか。同じことを
void CEsa::DrawEsa(CDC *pDC)
{
CInduDoc *pDoc = (CInduDoc *)((CFrameWnd *)AfxGetMainWnd())->GetActiveDocument();
で行ったときはうまくいったんですが。2つのメンバ関数の違いは、呼び出される場所で、InitCEsa()は
CInduDoc::CInduDoc() //CInduDocのコンストラクタ
の中の
InitGame(); //CInduDocクラスのメンバ関数です
の中のにある
m_esa.InitCEsa(); //m_esaはCInduDocクラスにあるCEsa型のメンバ変数です
で呼び出されてます(すこしわかりにくくてすみません)。つまり、CInduDocクラスのコンストラクタ内の最後の方で
呼び出される感じです。CInduDocクラスのコンストラクタ時点ではウィンドウがまだできてないなどということが
あるんでしょうか。
>ドキュメントクラスで変数を宣言する方法で良いと思いますよ。
>Viewと関連づけがされているのでViewに絡む部分の扱いが容易ですから。
やっぱりそうですか。確かに言われてみればそうですよね。
また、すこし長くなってしまいましたが、よろしくお願いします。
> CInduDoc::CInduDoc() //CInduDocのコンストラクタ
> の中の
> InitGame(); //CInduDocクラスのメンバ関数です
> の中のにある
> m_esa.InitCEsa(); //m_esaはCInduDocクラスにあるCEsa型のメンバ変数です
>
> で呼び出されてます(すこしわかりにくくてすみません)。つまり、CInduDocクラスのコンストラクタ内の最後の方で
> 呼び出される感じです。CInduDocクラスのコンストラクタ時点ではウィンドウがまだできてないなどということが
> あるんでしょうか。
このような方法(ドキュメントクラスのコンストラクタ中でポインタ所得)は出来なかったはずです。
これは、MFCの処理(フレーム、ドキュメント、ビューの各作成)を見てみれば判るのですが・・・
確か、MicrosoftのMastering Development C++(ずいぶん昔のソフトですので現在も入手できるかは不明)
でMFCの初期化処理等の説明があったと思います。(私もDOS→Winの時にお世話になりました)
はまちさんの周りで所有している人が見えるなら一度、使用させてもらえば結構役立つと思います。
やっぱり私自身の知識が少ないことが大きな原因のようですね。まだまだ勉強しなくてはいけないことがたくさんある
ようで少し途方にくれましたが、地道に長い時間をかけて、MSDNあたりからゆっくりやっていこうと思います。今回
は、いろいろと本当にありがとうございました。とても勉強になりました。
最後にもしわかればもう一つだけ教えていただきたいですが、マクロで2重定義を回避する方法なんですが、この方法と
いうのはよく使う方法なんでしょうか。アプリケーションをオブジェクト化していくと、必ず二つのクラスで互いにデ
ータを変更しあうということがあるはずだと思います。それなら、もうちょっとそういった話を見かけてもいいと思っ
たんですが、重さんの話だと、あまり書籍では見かけないとのことですし。むしろ、オブジェクト指向のプログラミン
グをする場合は、片方のクラスからもう一方のクラスへと一方向にする場合が多いのでしょうか。普通に考えれば、そ
んなはずはないと思うのですが、、、。オブジェクト指向の考えがまだまだ漠然としているものでイメージできなくて
申し訳ないです。
> 最後にもしわかればもう一つだけ教えていただきたいですが、マクロで2重定義を回避する方法なんですが、この方法と
> いうのはよく使う方法なんでしょうか。
よく使います。と、言うか使っているはずです。
AppWizardを使用したときのヘッダーファイルに
#if !defined(AFX_SQ_WINDOC_H__159E776A_85D6_11D5_A0EB_00909911BDE1__INCLUDED_)
#define AFX_SQ_WINDOC_H__159E776A_85D6_11D5_A0EB_00909911BDE1__INCLUDED_
・
・
・
#endif // !defined(AFX_SQ_WINDOC_H__159E776A_85D6_11D5_A0EB_00909911BDE1__INCLUDED_)
こんな感じでマクロが書かれていると思います。
この場合、
#define AFX_SQ_WINDOC_H__159E776A_85D6_11D5_A0EB_00909911BDE1__INCLUDED_
が定義されていれば
#if !defined(AFX_SQ_WINDOC_H__159E776A_85D6_11D5_A0EB_00909911BDE1__INCLUDED_)
から
#endif
までの間は無視されます。
この為、2重定義が回避されます。
> アプリケーションをオブジェクト化していくと、必ず二つのクラスで互いにデ
> ータを変更しあうということがあるはずだと思います。それなら、もうちょっとそういった話を見かけてもいいと思っ
> たんですが、重さんの話だと、あまり書籍では見かけないとのことですし。むしろ、オブジェクト指向のプログラミン
> グをする場合は、片方のクラスからもう一方のクラスへと一方向にする場合が多いのでしょうか。
私の場合、互いにデータを変更しあうといった操作はあまり無いです。
あるクラスからドキュメントクラスに対し「このデータを変更して」、「このデータを教えて」
といった感じに「ドキュメントクラスにアクセスする」事はあっても、「ドキュメントクラスからアクセスする」
事は無いです。
双方向でデータを変更し合うと規模が大きくなった時、データの正当性を取ることが難しくなりますから。
>私の場合、互いにデータを変更しあうといった操作はあまり無いです。
>あるクラスからドキュメントクラスに対し「このデータを変更して」、「このデータを教えて」
>といった感じに「ドキュメントクラスにアクセスする」事はあっても、「ドキュメントクラスからアクセスする」
>事は無いです。
>双方向でデータを変更し合うと規模が大きくなった時、データの正当性を取ることが難しくなりますから。
ありがとうございます。だいぶわかってきてうれしいです。もう一息考えてみます。