__closure
C++ 拡張キーワード への移動
カテゴリ
構文
<type> ( __closure * <id> ) (<param list>);
説明
C++ で利用できる関数ポインタには 2 種類のものがあります。1 つ目は、グローバル関数(クラスのメンバではない)を指す単純なポインタです。クラスのメンバ関数の場合には、メソッドの単純なポインタを使ってただ呼び出すことはできません。実際には 2 つのポインタが必要になります。元のオブジェクトを指すポインタと、メソッドのアドレスを指すポインタです。
VCL コンポーネント イベントはすべて、イベントが発生したときにクラスのメソッドを呼び出せるよう、クロージャとして宣言されます。次のコードは Classes.hpp ヘッダーの一部で、VCL の多くのイベント(OnClick など)で使われている TNotifyEvent の宣言を示しています。
typedef void __fastcall (__closure *TNotifyEvent)(System::TObject* Sender);
次の例は、通常のグローバル関数のポインタ型の宣言方法を示しています。
typedef void (*PointerToAFunction)();
これは次のように使用します。
/* Assigning a function to the function pointer */ PointerToAFunction a = SomeGlobalFunc;
この関数ポインタ型をメソッド ポインタにするには、次のように __closure を付け加えます。
typedef void (__closure *PointerToAFunction)();
このメソッド ポインタは次のように使用します。
/* Assigning a method to the method pointer */ PointerToAFunction a = &(someObject.SomeMemberFunc);
例
__closure キーワードはメンバ関数の特殊なポインタ型を宣言するために使用されます。標準の C++ では、メンバ関数のポインタを取得する唯一の方法は、次の例で示すように、完全修飾メンバ名を使用することです。
class base {
public:
void func(int x) {
};
};
typedef void(base::*pBaseMember)(int);
int main(int argc, char * argv[])
{
base baseObject;
pBaseMember m = &base::func; // Get pointer to member ‘func’.
// Call ‘func’ through the pointer to member.
(baseObject.*m)(17);
return 0;
}
ただし、派生クラスのメンバのポインタを基底クラスのメンバのポインタに代入することはできません。この規則("反変性" と呼びます)を次の例で説明します。
class base {
public:
void func(int x) {
};
};
typedef void(base::*pBaseMember)(int);
class derived : public base {
public:
void new_func(int i) {
};
};
int main(int argc, char * argv[]) {
derived derivedObject;
pBaseMember m = &derived::new_func; // ILLEGAL
return 0;
}
拡張キーワード __closure では、この制限を回避できるだけではありません。クロージャを使用すると、オブジェクト(たとえば、クラスの特定のインスタンス)のメンバ関数のポインタを取得できます。オブジェクトは、その継承階層に関係なく、どのようなオブジェクトでもかまいません。closure でメンバ関数を呼び出すときにオブジェクトのポインタが自動的に使用されます。次の例では、クロージャの宣言および使用方法を示しています。前の例で示した基底クラスと派生クラスは定義されているものと仮定します。
class base {
public:
void func(int x) {
};
};
typedef void(base::*pBaseMember)(int);
class derived : public base {
public:
void new_func(int i) {
};
};
int main(int argc, char * argv[]) {
derived derivedObject;
void(__closure * derivedClosure)(int);
// Get a pointer to the ‘new_func’ member.
// Note the closure is associated with the
// particular object, ‘derivedObject’.
derivedClosure = derivedObject.new_func;
derivedClosure(3); // Call ‘new_func’ through the closure.
return 0;
}
コード例については、「TRegExReplace(C++)」を参照してください。
クロージャは次の例で示すとおり、オブジェクトのポインタでも機能します。
class base {
public:
void func(int x) {
};
};
typedef void(base::*pBaseMember)(int);
class derived : public base {
public:
void new_func(int i) {
};
};
// Closure with pointer to object
void func1(base * pObj) {
// A closure taking an int argument and returning void.
void(__closure * myClosure)(int);
// Initialize the closure .
myClosure = pObj->func;
// Use the closure to call the member function.
myClosure(1);
return;
}
int main(int argc, char * argv[]) {
derived derivedObject;
void(__closure * derivedClosure)(int);
derivedClosure = derivedObject.new_func; // Same as before...
derivedClosure(3);
// We can use pointers to initialize a closure, too.
// We can also get a pointer to the ‘func’ member function
// in the base class.
func1(&derivedObject);
return 0;
}
ここでは、派生クラスのインスタンスのポインタを渡し、それを使用して基底クラスのメンバ関数のポインタを取得していますが、この方法は標準の C++ では使用できないことに注意してください。
クロージャは C++Builder RAD Studio 環境の主要な要素になっています。クロージャを使用することにより、[オブジェクト インスペクタ]でイベント ハンドラを割り当てることができます。たとえば、TButton オブジェクトには OnClick というイベントがあります。TButton クラスでは、OnClick イベントは、__closure 拡張キーワードを宣言で使用するプロパティです。__closure キーワードにより、別のクラスのメンバ関数(通常は TForm オブジェクトのメンバ関数)をプロパティに割り当てることができます。TButton オブジェクトをフォームに配置してから、ボタンの OnClick イベントのハンドラを作成すると、C++Builder により、ボタンの親である TForm にメンバ関数が作成され、そのメンバ関数が TButton の OnClick イベントに割り当てられます。このようにして、イベント ハンドラが TButton の他ならぬそのインスタンスに関連付けられます。