marunomaruno-memo

marunomaruno-memo

[C++] メンバー関数の関数ポインター

2014年06月13日 | C / C++
                                                        2014-06-17 追記
                                                        2014-06-13 新規
[C++] メンバー関数の関数ポインター
========================================================================

(MinGW gcc (GCC) 4.5.4)

クラスのメンバー関数への関数ポインターの定義と使用。


□ funcpointer.cpp
---
#include <iostream>
using namespace std;

class A {
private:
    static int staticdata;
    int data;

public:
    A(int data) :
            data(data) {
    }

    static int staticfunc(int data) {
        return data - staticdata;
    }

    int func(int data2) {
        return data + data2;
    }
};

int A::staticdata = 777;

int main() {
    A a(111);

    cout << "staticfunc(): " << A::staticfunc(999) << 
endl;
    cout << "func(): " << a.func(222) << endl;

    cout << " 関数ポインター " << endl;
    int (*sfp)(int data) = A::staticfunc;   // (1) static 関数ポインター
    cout << "staticfunc(): " << sfp(888) << endl;   //
 (2)

    int (A::*fp)(int data2) = &A::func; // (3) インスタンス関数のポ
インター
    cout << "func(): " << (a.*fp)(333) << endl; // (4)

    return 0;
}
---



□ 実行結果
---
staticfunc(): 222
func(): 333
関数ポインター
staticfunc(): 111
func(): 444
---

(1) static 関数ポインターの定義と初期化

    int (*sfp)(int data) = A::staticfunc;

通常のグローバル関数の関数ポインターと同じように定義する。
関数ポインターは、
    クラス名 :: 関数名
という形で取得する。

定義部分には、クラス名の要素が入っていないので、他のクラスでも同じシグネ
チャーの関数なら、この関数ポインターを使うことができる。

たとえば、B クラスに同じシグネチャーの
    static int staticfunc(int data)
があれば、
    sfp = B::staticfunc;
として、関数ポインターを設定できる。


(2) static 関数ポインターの利用

    cout << "staticfunc(): " << sfp(888) << endl;

通常のグローバル関数の関数ポインターと同じように利用すればよい。


(3) インスタンス関数ポインターの定義と初期化

関数ポインター名の前に、「A::」をつけておく。

    int (A::*fp)(int data2) = &A::func;

スコープ解決しないと、つぎのコンパイルエラーが出る。
    cannot convert 'int (A::*)(int)' to 'int (*)(int)' in initialization

関数ポインターは、
    & クラス名 :: 関数名
という形で取得する。「&」がないとコンパイルエラーになる。
    invalid use of non-static member function 'int A::func(int)'


(4) インスタンス関数ポインターの利用

    cout << "func(): " << (a.*fp)(333) << endl;

インスタンス a を使う。したがって、少し、違和感はあるが、次のような形で
利用する。
    (インスタンス名 .* 関数ポインター名)(引数のリスト)

インスタンスがないと使えないので、
    fp(333) 
では、つぎのコンパイルエラーが出る。
    must use '.*' or '->*' to call pointer-to-member function in 
'fp (...)', 
    e.g. '(... ->* fp) (...)'

もちろん、クラス A のメンバー関数用なので、まったく同じシグネチャーを持
つ別のクラスのメンバー関数のインスタンスで使おうとしても、コンパイルエ
ラー。
    B b;
    (b.*fp)(333)
では、つぎのエラー。
    pointer to member type 'int (A::)(int)' incompatible with object 
type 'B'



■ メンバー関数ポインターのポリモーフィズム (2014-06-17 追記)

上記 A クラスを継承した D クラスをつきのように定義する。

---
class D : public A {
public:
    D(int data) :
            A(data) {
    }

    int func(int data) {
        return data * 2;
    }

};
---

このメンバー関数 func() は、基底クラス A のメンバー関数 func() をオー
バーライドしている。
この D クラスのインスタンスを基底クラス A 型として扱う。

つぎのようなコードを上にサンプルに追加する。
---
    A* d = new D(11);   // (5)
    fp = static_cast<int (A::*)(int)> (&D::func);   // (6)
    cout << "func(): " << (d->*fp)(333) << endl; // (7)
---

この部分の実行結果としては、つぎのようになる。
---
func(): 666
---

A クラスの型だが、D クラスのメンバー関数 func() を実行しているのが確認で
きる。



(5) A クラスの型として、D クラスのインスタンスを生成する。

    A* d = new D(11);   // (5)


(6) D クラスのオーバーライドしているメンバー関数 func を、A クラスの関数
ポインターに代入する

    fp = static_cast<int (A::*)(int)> (&D::func);   // (6) 

このとき、アップキャストすることになるが、不思議なことに、static_cast が
必要。


(7) D クラスのインスタンスを使って、この関数ポインターが指す関数を実行す
る

    cout << "func(): " << (d->*fp)(333) << endl; // (7)


インスタンスのポインター d を使う。したがって、少し、違和感はあるが、次
のような形で利用する。
    (インスタンスのポインター名 ->* 関数ポインター名)(引数のリスト)


                                                                   以上
---
2014-06-17 追記 メンバー関数のポリモーフェィズム
---



最新の画像もっと見る

コメントを投稿