< Oracle のコメント | atoin 関数 >

April 27, 2004

メンバへのポインタ演算子 ってやつ

メンバへのポインタ演算子ってやつがある。MSDNライブラリでの説明はこう。

メンバへのポインタ演算子: .* および ->*
pm-expression :
cast-expression
pm-expression .* cast-expression
pm-expression ->* cast-expression
二項演算子 .* は第 1 オペランドを第 2 オペランドに結合します。第 1 オペランドはクラス型のオブジェクトとし、第 2 オペランドはメンバへのポインタ型とする必要があります。
二項演算子 ->* も第 1 オペランドを第 2 オペランドに結合します。この第 1 オペランドはクラス型のオブジェクトを指すポインタ、第 2 オペランドはメンバへのポインタ型にする必要があります。
演算子 .* を使った式の第 1 オペランドの型は、第 2 オペランドで指定した、メンバへのポインタと同じクラス型にするか、そのクラスから明確に継承させたあいまいさのない型にする必要があります。
演算子 ->* を使った式の第 1 オペランドの型は、第 2 オペランドで指定した型の "クラス型へのポインタ" にするか、そのクラスから明確に継承させたあいまいさのない型にする必要があります。

この説明で、は、はーん、ってわかった人いますかね。私はわかりませんでした。

この演算子が何なのか、何でこんなものいるか、というと、まず、クラスのメンバ関数を、関数ポインタからコールしたい、という場合があるとします。クラスのメンバ関数も関数なので、もちろん関数ポインタをとることができます。たとえばこんな感じ。

#include <iostream>
using namespace std;

// Bird クラスです。
class Bird
{
public:
    int Tweet (char* message)
    {
        cout << message << endl;
        return 1;
    }
};

// で、ポインタを取ってみます。
int main (int argc, char** argv)
{
    int (Bird::*fn) (char*);    // Bird クラスのメンバ関数ポインタ型変数 fn を宣言
    fn = Bird::Tweet;           // 代入
    
    return 0;
}

これを呼び出すにはどうするか、っていうと、普通の関数ポインタとして呼び出してもしかられてしまいます。

int main (int argc, char** argv)
{
    int (Bird::*fn) (char*);    // Bird クラスのメンバ関数ポインタ型変数 fn を宣言
    fn = Bird::Tweet;           // 代入
    
    int ret = fn ("ちゅんちゅん");    // 呼び出してみた
    return 0;
}

コンパイル中...
foo.cpp
c:\work\bird\bird.cpp(21) : error C2064: 関数ポインタとして評価されない式を使って、関数を呼び出そうとしました。
cl.exe の実行エラー
ブラウザ データベースを作成中...

bird.exe - エラー 1、警告 0

ところで、メンバ関数の呼び出しと、通常の関数の呼び出しは何が違うか。そうそう。メンバ関数には、 this ポインタってもんが渡されますね。上の呼び出しでは、this ポインタの実体がないし、何を渡していいのかわかりません。まさしく、メンバへのポインタ演算子は、this ポインタを渡すためのものです。

int main (int argc, char** argv)
{
    int (Bird::*fn) (char*);    // Bird クラスのメンバ関数ポインタ型変数 fn を宣言
    fn = Bird::Tweet;           // 代入
    
    Bird    bird;               // とりあえず、実体を作りました
    int ret = (bird.*fn) ("ちゅんちゅん");  // メンバへのポインタ演算子を使ってみます。
    return 0;
}
ちゅんちゅん
Press any key to continue

これが正解です。クラスの実体(C++でもインスタンスって言うのかしら)へのポインタから呼び出す場合は、->* で同じように呼び出すわけです。ところで、(foo.*fn) のように括弧で括って、その後ろに引数のカッコをつけているのは、引数のカッコ、すなわち関数呼び出し演算子の方が、メンバへのポインタ演算子より結合の優先順位が高いためです。必ずカッコで括らなきゃいけません。

トラックバック

このエントリーにトラックバック:
http://frog.raindrop.jp/cgi-bin/mt/mt-tb.cgi/348

コメント

コメントする

※ コメントスパム対策のため、コメント本文はおはよう、こんにちわ、こんばんわのいずれかより始めるようにしてください。

name:
email:

※ 必要ですが、表示しません。

url:
情報を保存する ?