- ベストアンサー
クラスのthisとは
前の質問で クラスにコールバック関数を取り入れる事を質問してきました。 http://oshiete1.goo.ne.jp/kotaeru.php3?q=2310776 コールバック関数をクラス化させる方法については分かりましたが、 Win32に渡す引数のところになぜ(void*)thisをいれたら解決できなのでしょうか? そもそもthisのとはなにか?なぜクラスにはこのようなのが必要なのか不思議です。 thisとは何者なのでしょうか?
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
とりあえず引き続き登場してみました。 とは言え、今回のような物の説明となると私は苦手なんですけどね。正直。 this ですが 「this ポインタ」って大抵は言います。参考URLに「」でくくった 単語で検索した最初のページをあげておきます。この単語で検索すると結構出てくるので 一通り調べておくと良いでしょう。 コレは自身を指し示したポインタです。そしてこのthisポインタは省略こそされていますが クラスメンバには本来は必ずついているものです。 つまり class CMyClass { public: int miParam; void test( void ); } このようなクラスがあり test() の中身が void CMyClass::test( void ) { miParam = 1000; } だった場合、実は省略されていますが実際には void CMyClass::test( void ) { this->miParam = 1000; } と書かれているのと同じです。まあ普段からいちいちクラス自身のメンバ変数にアクセス するのにthisを書かなければいけないのは面倒ですからね。 じゃあ何故自身を指し示すポインタが必要なのか・・・例えば void CMyClass::test( void ) { int miParam; miParam = 1000; } まあこんなことしませんが、仮にこう書いた時 miParam はクラスと、ローカルの値 どちらに代入すると思いますか?.NET2003ではローカルの方の値に入れていました。 コレを void CMyClass::test( void ) { int miParam; this->miParam = 1000; } こう書くと、クラスの方の変数に代入されるハズです。VisualC++ があるなら デバッカを上手く使用すると分かりやすいです。 で、先のコールバックに何ゆえthisを入れればOKだったのか・・・ですが・・・ 上手説明できるかな??不安ですが・・・・・・ コールバックはグローバルもしくは static で無いとダメってのは、クラスってのは作ってしまうと 何個でも生成できますよね?つまり CMyClass obj[4]; って書いた場合、コンパイラは、じゃあコールバックで使いたい CMyClass::test(); って一体 [0][1][2][3] どれの CMyClass::test(); のことよ?ってなってしまいます。だって変数は宣言した分存在してますよね? 関数も極端に言えば同じです。いくつも宣言されてしまうんです。だから最初のエラーは出ています。 よって、それの混乱を防ぐ為に グローバル、もしくはstatic で静的として宣言すれば test(); は必ず一つ しかない物になるのでコールバックとして正しく使用できるとなります。(ここら辺はNo1さんの言ってることが正解です 私の言い方だと、実はちょっとおかしいです。これだとコールバックにthisを入れればOKになるんで 上手く簡単に説明できません。ゴメンナサイ、難しいです・・・・) そして this ですが、同じく同じクラスを4つ宣言したとして、じゃあその一つしかないコールバックで [0][1][2][3]どれ使えばいいのよ?とにかく宣言した自分自身を使いたいんだよ!!となった時に明示的に this を入れたことにより、「自分自身のポインタ」から見た miParam を正しく参照できるわけです。 な・・・長い・・・つーか改めて文章で説明となるとこれほど難しいとは・・・・・・ わかりずらかったらほんとゴメンナサイ。 もっと簡単に説明した場所あるかな・・・・
その他の回答 (3)
- MrBan
- ベストアンサー率53% (331/615)
ちなみに、コールバックというのは一般的なプログラミング手法の一種です。 WindowsのDLLを他言語から呼ぶ場合の規約はCALLBACK(C言語でいえばstdcall)を使うルールですので、WindowsのAPIのようなC++以外の言語も呼ぶものにはstdcallが使われています。 そして、サードパーティ製コードもこれに習っていることが多いです。 ですが、C++としてはクラスのメンバ関数のポインタに対してコールバックすることも可能です。 そういうポインタの指定方法もあります。(ポインタ指定も必要ですし使いやすいとは言えないですが) ただし、thiscallのコールバックは、現実的にC++コンパイラ依存のものになります。 なお、識別は変数で行うため、呼び出し規約がどうであろうと、コード領域の関数が複数宣言されることはありません。 staticで一つ~というのは説明用のアヤでしょう。 メンバ関数がエラーになるのは、thiscallの関数ポインタはコールバックの要求と型が合わないからに過ぎません。 グローバルやstaticで、CALLBACK(stdcall)の指定をしなければならないのも同じ理由です。
お礼
長くありがとうございました。 またよろしくおねがいします。
- MrBan
- ベストアンサー率53% (331/615)
前の質問の場合、thiscallとstdcallの違いとか呼び出し規約とかは特に関係がない話です。 提示のコードは、コールバック関数の引数にクラスのポインタを渡してくれるように期待しているものをぱくってます。 そこにNULLを渡したら、コールバック関数の引数でpContextにそのNULLが渡されるので、 当然コールバック関数内でNULLポインタ参照します。 だから動かない。 > CInputDevice* pDevice = (CInputDevice*) pContext; 怪しいのはここ。 きちんとNULLチェックすれば、ここでpContextがNULLなことがわかるはずです。 サンプルは簡便にするためにエラーチェックを省略してることもありますが、むやみに省略しないことをお勧めします。 そして、当該クラスのポインタを渡してもらうようにできているコードに、そのクラスのthisポインタを渡せば正しく動く、ということです。 意味もわからずコピペのツギハギをした結果だと思います。 なお、普段はあまり意識しませんが(Windows環境の)関数呼び出し規約はたとえば以下のようなものがあります。 thiscall(メンバ関数の呼び出し規約)、 stdcall(CALLBACKマクロの正体、Windowsの標準規約)、 cdecl(可変長引数が使えるC言語の標準規約)、 fastcall(CPU依存でBorland等がよくつかった規約) 呼び出し規約とはいうなればアセンブリレベルのスタック操作手順です。 これらはそれぞれ長所短所があり、互いに互換性がありませんので、違う型と認識してコンパイルエラーになります。 既に回答があるように、thisとはそのクラスのインスタンスを指すポインタであり、thiscallの関数では自動で生成されます。 グローバル関数やクラスのstatic関数はthiscallではないので、thisは使えません。 コンパイルオプションにもよりますが、 明示的にCALLBACK(stdcall)等を指定しないとcdeclと解釈されますのでコンパイルが通りません。
- jacta
- ベストアンサー率26% (845/3158)
すごく乱暴に説明すると、 class A { public: int func(int arg); private: int x; }; というクラスがあって、 A a; a.func(123); のように呼び出すとします。 これは内部的には、 int A__func__i(A* this, int arg); // A::funcの正体 という関数を、 A__func__i(&a, 123); のように呼び出しているのと大体同じになります。 つまり、a.func(123)というメンバ関数の呼び出しにおいては、aへのポインタがthisという仮引数に渡されると考えてほぼ間違いありません。 そして、&aがthisとして渡されるからこそ、this->xをメンバ関数の中で参照することができるのです。
お礼
乱暴だから結構わかりやすいって事です。 基本が良く分かりました! 未熟者ですがまた何かありましたらよろしくねがします。 ありがとうございました。
お礼
なにか先生みたいになっちゃって、非常にお世話になっています。 おかげで、良く分かりました。 というわけでこの問題については幕を閉じます。 たぶん、まだまだ未熟者ですので何回も質問することあると思いますが、そのときはよろしくお願いします。 影の先生さん!どうもありがとうございました。