marunomaruno-memo

marunomaruno-memo

[C++] メンバー関数の関数ポインターをメンバー変数にする

2014年06月20日 | C / C++
                                                        2014-06-20 新規
[C++] メンバー関数の関数ポインターをメンバー変数にする
======================================================================

(MinGW gcc (GCC) 4.5.4)

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


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

class A {
private:
    int data;
    int (A::*fp[2])(int data);  // (1) インスタンス関数のポインター

public:
    A(int data) :
            data(data), fp({&A::func, &A::func2}) { // (2)
    }

    int exec(int index, int data) {
        return (this->*fp[index])(data);    // (3)
    }

private:
    int func(int data) {
        return this->data + data;
    }
    int func2(int data) {
        return this->data - data;
    }
};

int main() {
    A a(100);
    cout << "0: " << a.exec(0, 10) << endl; // (4)
    cout << "1: " << a.exec(1, 20) << endl;

    return 0;
}
---

□ 実行結果
---
0: 110
1: 80
---

(1) インスタンス関数ポインターの配列

通常のメンバー関数ポインターの配列と同じ。

    int (A::*fp[2])(int data);  // (1) インスタンス関数のポインター


(2) インスタンス関数ポインターの配列の初期値

    A(int data) :
            data(data), fp({&A::func, &A::func2}) { // (2)
    }

コンストラクターの初期化リストで、
    fp({&A::func, &A::func2})
ということはできないかと思ったが、どうやらできる。
ただし、つぎの警告が出る。
    extended initializer lists only available with -std=c++0x or 
    -std=gn
u++0x
コンパイルするときに上記のオプションを指定すれば、警告は出なくなる。


(3) 関数ポインターの配列を実行するメンバー関数

    return (this->*fp[index])(data);    // (3)

this ポインターを使わないといけない。使わずに、
    (fp[index])(data)
とすると、つぎのコンパイルエラー。
    must use '.*' or '->*' to call pointer-to-member function in 
    '((A*)this)->A::fp[index] (...)', e.g. 
    '(... ->* ((A*)this)->A::fp[index]) (...)'

メンバー関数のポインターは、".*" か "->*" を使わないといけないからかな。


(4) 実行

たんに、メンバー関数を呼んでいるだけ。最初の引数はメンバー関数のポイン
ターの配列の添字を指定している。
    cout << "func(): " << a.exec(0, 10) << endl; // (4)

メンバー関数のポインターの配列 fp を public にして、main() 関数でつぎの
ように使おうとしても、
    (a.*fp[0])(30)
コンパイルエラー。
    'fp' was not declared in this scope
まあ、上記の書き方だと、main 関数の中で fp を定義しないとだめだ。


                                                                   以上


[C++] 関数テンプレートのポインター

2014年06月19日 | C / C++
                                                        2014-06-19 新規
[C++] 関数テンプレートのポインター
========================================================================
(MinGW gcc (GCC) 4.5.4)

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

template<class T>   // (1)
T square(T a) {
    return a * a;
}

template<class T, class F>  // (2)
T calc(T a, F func){
    return func(a);
}

int main() {
    int (*p)(int) = square; // (3) この使い方はあまり意味ない感じ
    cout << p(2) << endl;   // (4)
    cout << p(3.4) << endl; // (5) 

    cout << calc(2, square<int>) << endl;   // (6) 
    cout << calc(3.4, square<double>) << endl;  // (7)
//  cout << calc(5, square) << endl;    // コンパイルエラー

    return 0;
}
---

□ 実行結果
---
4
9
4
11.56
---

(1) 関数テンプレートの宣言

(2) 関数テンプレートのポインターを引数に持つ関数テンプレートの宣言

(3) 関数テンプレートを実体化したものを関数ポインターに設定

関数ポインターは、通常の関数ポインターと変わりはない。テンプレートの型は
自分で設定している。
    int (*p)(int) = square; // (3) この使い方はあまり意味ない感じ


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

int 型の引数を取るので、そのまま使う。
    cout << p(2) << endl;   // (4)

int 型の引数を取るので、double 型の引数を渡しても、int 型として処理され
る。
    cout << p(3.4) << endl; // (5) 


(6)(7) 関数テンプレートのポインターを引数に持つ関数テンプレートの利用

int 型を明示しているの、int 型の引数の square 関数ポインターを渡す。
    cout << calc(2, square<int>) << endl;   // (6) 

double 型を明示しているの、double 型の引数の square 関数ポインターを渡す。
    cout << calc(3.4, square<double>) << endl;  // (7)

つぎのように、データ型を明示していないと、コンパイルエラーになる。
    calc(5, square)

    no matching function for call to 'calc(int, <unresolved 
    overloaded function type>)'

                                                                   以上


[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 追記 メンバー関数のポリモーフェィズム
---