ポインタについての質問です
たとえばCxxxDlgというダイアログクラスで
hファイル内で
DECLARE_MESSAGE_MAP()
があって
cppファイル内で
BEGIN_MESSAGE_MAP(CxxxDlg, CDialog)
ON_BN_CLICKED(IDC_BUTTON1, OnBnClickedButton1)
END_MESSAGE_MAP()
とあるとしますが、
ここで OnBnClickedButton1 と記述してあるものは
関数ポインタの事を指すと思います
気になっているのは
ここの定義の部分はstaticで
このポインタはCxxxDlgクラスのメンバ関数ポインタで
&CxxxDlg::OnBnClickedButton1
としなければいけないような気がするのですが
何か秘密があるのでしょうか?
ご教授ください
よろしくお願いします
マクロなんだから
ON_BN_CLICKED(IDC_BUTTON1, OnBnClickedButton1)
これがどう展開されるか確認してから質問するのがスジちゃいますか?
ON_BN_CLICKEDは最終的には
#define ON_BN_CLICKED(id, memberFxn) \
ON_CONTROL(BN_CLICKED, id, memberFxn)
{ WM_COMMAND, (WORD)BN_CLICKED, (WORD)id, (WORD)id, AfxSigCmd_v, \
(static_cast< AFX_PMSG > (memberFxn)) },
と展開されていると思います
ただ、memberFxnには OnBnClickedButton1 と入っていまして
メンバ関数ポインタを指す形式の &CxxxDlg::OnBnClickedButton1 ではありません
static 部分ですからスコープは外れていて
これだと グローバルのOnBnClickedButton1() というものを
探しにいくと思うんですけど
探しにいかず ちゃんと CxxxDlg::OnBnClickedButton1() にたどり着きます
どこか見落としているマクロがあるんでしょうか?
正直わかんないです。
VC++8では
ON_BN_CLICKED(IDC_BUTTON1, &XXX::OnBnClickedButton1)
となってますので。
はい
ON_BN_CLICKED(IDC_BUTTON1, &XXX::OnBnClickedButton1)
という書き方であれば、疑問はどこにもなかったのですが
自分は VC++.net の環境ですが
ON_BN_CLICKED(IDC_BUTTON1, OnBnClickedButton1)
となります
どこかスコープを操作している部分があるのか、
いくら目にとまった、マクロ等を展開していっても
自分の能力の無さか、たどり着けませんでした
きっと何か隠れているような気がしてなりません
# VCのバージョンは?
BEGIN_MESSAGE_MAP マクロの実装を調べてみてください。
確かに昔はXXX::ついてなかったね。
以下はVC6で試した。
VC8はあるけどVC7.x系はインストールに失敗するので使ってない。
ここはメンバ関数へのポインタでありキャストしても
グローバル(メンバ関数じゃない)関数へのポインタを
メンバ関数へのポインタをキャストすることは
VC6でもreinterpret_castでもコンパイルエラーになるんじゃない。
上記でメンバ関数なら可能と判断した場合は
const HOGE XXX::data[] = { &メンバ関数 , ~ };
では、XXX::メンバ関数が候補になるようですね。
const HOGE XXX::data[] = { &YYY::メンバ関数 , ~ };
としたいならYYYは省略できない。
C++の規格としてはこういう規則なのか知りませんが。
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
と宣言がありまして、このメンバ関数ポインタに代入するようです
メンバ関数ポインタですので、基本的には普通の関数ポインタは代入できないと思いま
す。
ただし、スコープ内にあれば、グローバルでなくても候補として XXX:: のメンバを探す
と思います。
ただ、この配列に代入するに当たって、この配列は static にて宣言されていて、
スコープは届かないと思うんです。
不思議です。namespace 的なものもマクロからは見つけられません。
特殊な仕様であって仕方ないと考えるしか、ないのでしょうか?
知らなくてもいい部分かも知れませんが、異常に気になって先に進めません・・・
A型です
> ただ、この配列に代入するに当たって、この配列は static にて宣言されていて、
> スコープは届かないと思うんです。
なんで? その配列が XXX のメンバであるならXXXスコープにあるんちゃう?
同じ名前があっても
コンパイラがあいまいと判断しなかったら
コンパイルエラーにならないからな。
bar boo::foo[] = { &hoge };
の{}中はbooのスコープになるということなのかな。
でも検索したけどそれらしい説明に触れているところは見つからなかった。
boo::foo[1] = &hoge;
は、booのスコープにならなかった。
VC6の結果論ですけど。
> なんで? その配列が XXX のメンバであるならXXXスコープにあるんちゃう?
その配列は static です。
static はある意味グローバルだと思うんですが、
そうでないものはインスタンスがない限り見えないというかアクセスできないと思いま
す
見えない this ポインタを存在させるために、メンバポインタやメンバ関数ポインタな
どの
形があると思っています
Cxxx::*yyy という形で Cxxx クラス内のメンバポインタを宣言し、
&Cxxx::zzz という形で ポインタを取得する
これはおそらく 内部的に this ポインタからのオフセット的な意味合いがあって、
どこにインスタンスが生成されても正しいインスタンスを見つける仕組みではないか
と・・・
では、static ではないメンバポインタへの参照を、普通の参照方法で
正しいインスタンス内のメンバをどうやって参照できるのか、できないと思います。
と思ってましたが、間違っていますでしょうか?
えと、VC6でのお話ですよね。単にコンパイラの不備じゃないすか?
VC8だと
#include <iostream> // cout, endl
class foo {
public:
typedef void (foo::*func)();
void f() { std::cout << foo::f\n; }
void g() { std::cout << foo::g\n; }
static func table[2];
};
foo::func foo::table[] = {
&foo::f, // これならOK
g // error
};
int main() {
foo f;
(f.*foo::table[0])();
}
でも、どう考えても C++ 的には間違いだと思います
おそらくどこかにクラスを偽装した namespace 的なものが存在し、
スコープを同一CPPファイル内まで拡張しているのかと考えていました
もしくは動的に生成されたクラス内の関数は普通ヒープに展開されていてると思います
で、static の部分は既にメモリ上にグローバルとして展開されていると思います
ですから、static 側からその他のメンバは参照できないと思います
そうすると static にしかけがあって、
実はグローバルを参照する変数に置き換えられているとか
いろいろ考えてはみましたが・・・
> えと、VC6でのお話ですよね。単にコンパイラの不備じゃないすか?
そういうことで納得しときます・・・
VC2005(VC8)でもコンパイル、リンクできるね。
紛らわしくするためにクラスと関係ないものを用意しても
void OnBnClickedButton1()
{
}
以下のどの書き方でも
ON_BN_CLICKED(IDC_BUTTON1, &CMyDlg::OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUTTON1, &OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUTTON1, OnBnClickedButton1)
クラス内の関数は普通ヒープに展開されないよ。
staticメンバ関数からstaticではないメンバを参照できないけど
メンバ関数へのポインタは参照できるよ。