C++ 拡張キーワード
このセクションでは、RAD Studio ライブラリをサポートするために C++Builder に実装されている拡張キーワードについて説明します。
__classid
__classid 演算子は、コンパイラで指定クラス名の vtable のポインタを生成するために使用されます。この演算子は、クラスからメタクラスを取得するために使用されます。
構文
__classid (classname)
たとえば、プロパティ エディタ、コンポーネント、クラスの登録時、および、TObject の InheritsFrom メソッドで __classid が使用されます。次のコードでは、TWinControl から派生した新規コンポーネントの作成に __classid が使用されている例を示します。
namespace Ywndctrl
{
void __fastcall PACKAGE Register() {
TComponentClass classes[1] = {__classid(MyWndCtrl)};
RegisterComponents("Additional", classes, 0);
}
}
__delphirtti
__delphirtti を使用すると、特定の Delphi 互換型の情報を実行時に取得することができます。__delphirtti 関数の機能は、Delphi で使用可能な TypeInfo 関数と同等です。
例:
TTypeInfo *ti = __delphirtti(TMyType);
関連項目
__closure
__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 では、この制限を回避できるだけではありません。クロージャを使用すると、オブジェクト(たとえば、クラスの特定のインスタンス)のメンバ関数のポインタを取得できます。オブジェクトは、その継承階層に関係なく、どのようなオブジェクトでもかまいません。クロージャを通じてメンバ関数を呼び出すと、そのオブジェクトのポインタが自動的に使用されます。次の例では、クロージャの宣言および使用方法を示しています。前の例で示した基底クラスと派生クラスは定義されているものと仮定します。
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 の他ならぬそのインスタンスに関連付けられます。
__property
__property キーワードはクラスの属性を宣言します。プログラマには、プロパティはクラスの他の属性(フィールド)と同じように見えます。ただし、Object Pascal の該当機能と同様に、C++Builder の __property キーワードには、属性の値を検査し変更するだけではなく、もっと機能が追加されています。プロパティ属性によりプロパティへのアクセスが完全に制御されるので、クラスそのものの内部でプロパティを実装する方法に制限はありません。
構文
__property type propertyName [index1Type index1 ][indexNType indexN ] = { attributes };
ここで、各要素の意味は次のとおりです。
<型>
は組み込みデータ型または既に宣言されているデータ型です。<プロパティ名>
は任意の有効な識別子です。<インデックス N の型>
は組み込みデータ型または既に宣言されているデータ型です。<インデックス N>
は、プロパティの読み取り関数や書き込み関数に渡されるインデックス パラメータの名前です。<属性>
は、read、write、stored、index の一部または全部をこの順にコンマで区切って並べたものです。
角かっこ内の <インデックス N> パラメータは省略可能です。これらが指定された場合は、配列プロパティを宣言します。インデックス パラメータは配列プロパティの読み取りメソッドと書き込みメソッドに渡されます。
以下は簡単なプロパティ宣言の例です。
class PropertyExample {
private:
int Fx, Fy;
float Fcells[100][100];
protected:
int readX() {
return (Fx);
}
void writeX(int newFx) {
Fx = newFx;
}
double computeZ() {
// Do some computation and return a floating-point value...
return (0.0);
}
float cellValue(int row, int col) {
return (Fcells[row][col]);
}
public:
__property int X = {read = readX, write = writeX};
__property int Y = {read = Fy};
__property double Z = {read = computeZ};
__property float Cells[int row][int col] = {read = cellValue};
};
上記の例では、プロパティ宣言をいくつか示しています。
- プロパティ X の読み取りと書き込みは、それぞれメンバ関数 readX と writeX を通じて行えます。
- プロパティ Y はメンバ変数 Fy に直接対応するもので、読み取り専用です。
- プロパティ Z は、計算で得られる読み取り専用値で、クラスにデータ メンバとして格納されません。
- Cells プロパティは、2 つのインデックスを持つ配列プロパティの例を示しています。
次の例では、ユーザー コードでこれらのプロパティにどうアクセスするかを示します。
// Previous code goes here
int main(int argc, char * argv[]) {
PropertyExample myPropertyExample ;
myPropertyExample.X = 42; // Evaluates to: myPropertyExample.WriteX(42) ;
int myVal1 = myPropertyExample.Y; // Evaluates to: myVal1 = myPropertyExample.Fy ;
double myVal2 = myPropertyExample.Z; // Evaluates to: myVal2 = myPropertyExample.ComputeZ() ;
float cellVal = myPropertyExample.Cells[3][7]; // Evaluates to : cellVal = myPropertyExample.cellValue(3,7);
}
プロパティには、他にも、この例には示されていない多くの種類や機能があります。
プロパティでは以下も可能です。
- インデックス属性を使って、同一の読み取りメソッドや書き込みメソッドを複数のプロパティに関連付けること。
- デフォルト値を持つこと。
- フォーム ファイルに格納されること。
- 基底クラスで定義されたプロパティを拡張すること。
__published
__publiched キーワードは、クラスがコンポーネント パレットにある場合、__published セクション内のプロパティが[オブジェクト インスペクタ]に表示されることを指定します。__published セクションを用意できるのは、TObject から派生したクラスだけです。
published メンバの可視性規則は public メンバの規則とまったく同じです。published メンバと public メンバの違いは、__published セクションで宣言されたデータ メンバやプロパティに対しては Object Pascal 形式の実行時型情報(RTTI)が生成される点だけです。RTTI により、RTTI がないと不明なクラス型のデータ メンバ、メンバ関数、プロパティをアプリケーションで動的に問い合わせることができるようになります。
メモ: __published セクションで使用できるのは、プロパティ、Pascal 組み込みのデータ メンバまたはライブラリに由来するデータ メンバ、メンバ関数、クロージャです。__published セクションで定義されるフィールドはクラス型であることが必要です。__published セクションで定義されるプロパティは、配列プロパティ以外でなければなりません。__published セクションで定義されるプロパティの型は、順序型、実数型、文字列型、小集合型、クラス型、メソッド ポインタ型のいずれかであることが必要です。