クラス メソッド
クラス:インデックス への移動
C++ において、クラス メソッドとは、クラスのインスタンスとクラス名で呼び出すことができるメソッドです。 それに対して、オブジェクト メソッドはオブジェクトつまりクラスのインスタンスのみで呼び出すことができます。
目次
Object Pascal でのクラス メソッドの動作
Object Pascal 言語(Object Pascal)は Object Pascal ヘルプ トピック「メソッド」の「クラス メソッド」セクションに説明されているとおりにメタクラスとクラス メソッドをサポートします。
ここで例を示します。
TTest = class;
// メタクラス型の宣言
TTestClass = class of TTest;
TTest = class
public
// クラス メソッド
class function add(I, J: Integer): Integer;
class function GetClsName: string;
// 仮想クラス メソッド
class function GetClsNameVirt: string; virtual;
// クラスの取得アクセサ メソッドと設定アクセサ メソッド
class function GetCount: Integer;
class procedure SetCount(I: Integer);
//仮想 クラスの取得アクセサ メソッドと設定アクセサ メソッド
class function GetStrProp: string; virtual;
class procedure SetStrProp(N: string); virtual;
// クラス static
class function GetStaticCount: Integer; static;
class procedure SetStaticCount(I: Integer); static;
// クラス プロパティ
property Count: Integer read GetCount write SetCount; // 非仮想
property StrProp: string read GetStrProp write SetStrProp; // 仮想取得メソッド / 設定メソッド
end;
// クラス参照をとる関数
function RegisterTestType(Cls: TTestClass): boolean;
明示的に TMetaClass* パラメータが指定された静的メソッドのクラス メソッド
Appmethod C++ 2009 より前では、クラス メソッドは明示的にメタクラス パラメータが指定された静的メソッドとして表現されていました。 メタクラスは TClass または TMetaClass インスタンスへのポインタで表現されます。 このメタクラス(クラス参照)は __classid 拡張で取得され、クラス名の TMetaClass* インスタンスが返ります。
次に使用例を示します。Object Pascal の前の例と同じプロパティやメソッドの定義を試みます。 Object Pascal のクラス メソッド機能の一部はこのメタクラスのアプローチでは正しく実行できないことに注意してください。
// すべてのメタクラスの型は C++ の TMetaClass* です。
typedef TMetaClass* TTestClass;
class DELPHICLASS TTest;
class PASCALIMPLEMENTATION TTest : public System::TObject
{
typedef System::TObject inherited;
public:
// クラス メソッドは第 1 パラメータとして明示された 'hidden' クラス参照のある
// 静的メソッドとして公開されます。
static int __fastcall add(TMetaClass* vmt, int I, int J);
static UnicodeString __fastcall GetClsName(TMetaClass* vmt);
// 仮想クラス メソッドは第 1 パラメータとして明示された hidden クラス参照
// のある "通常" の仮想メソッドとして公開されます。
// つまりこのメソッドを C++ から呼び出すとき、
// 2 つの 'hidden' パラメータが渡される必要があるので、動作しません。
virtual UnicodeString __fastcall GetClsNameVirt(TMetaClass* vmt);
// 非仮想メソッドは動作できます。 これらの 2 つのメソッドは
// __classid(TTest) にハードコードされた最初の TMetaClass* パラメータ
// で通常オーバーロードされます。
static int __fastcall GetCount(TMetaClass* vmt);
static void __fastcall SetCount(TMetaClass* vmt, int I);
// これらの仮想の設定アクセサ メソッドと取得アクセサ メソッドをオーバーロードできますが、
// 呼び出しが不正なので、これらのメソッドに
//関連付けられたプロパティにアクセスするとき、プログラムでエラーが発生します。
virtual UnicodeString __fastcall GetStrProp(TMetaClass* vmt);
virtual void __fastcall SetStrProp(TMetaClass* vmt, UnicodeString N);
// Delphi クラスの静的メソッドは標準の C++ の静的メソッドです。
static int __fastcall GetStaticCount();
static void __fastcall SetStaticCount(int I);
// コンパイラではこれらの宣言が許可されますが、
//TMetaClass* が必須なので、これらのプロパティに
// アクセスするときに C++ コンパイラからエラーが発生します。
__property int Count = {read=GetCount, write=SetCount, nodefault};
__property UnicodeString StrProp = {read=GetStrProp, write=SetStrProp};};
extern PACKAGE bool __fastcall RegisterTestType(TMetaClass* Cls);
明示的な TMetaClass* パラメータでクラス メソッドを公開するこの方法には複数の制限があります。
- C++ コードは仮想クラス メソッドを正しく呼び出すことができません。 C++ の呼び出しでは、2 種類の隠蔽パラメータ(オブジェクトのインスタンスへのポインタと明示的な TMetaClass* パラメータ)を渡す必要があります。 ただし、この関数で必要なのは TMetaClass* パラメータのみです。
- 仮想クラス メソッドへの呼び出しが成功する場合でも、C++ のコードにはメソッドを呼び出すためのオブジェクトのインスタンスが必要ですが、正しいクラス メソッドはオブジェクト インスタンスを必要とせずに呼び出し可能である必要があります。
- C++ のコードは取得アクセサ メソッドや設定アクセサ メソッドがクラス メソッドであるプロパティに正しくアクセスできません。これは明示的である必要がある TMetaClass* パラメータを渡す方法がないからです。
__classmethod キーワードを使用したクラス メソッド
Appmethod C++ 2009 では前に示した制限が取り除かれ、構文が簡単でさらに直感的になったクラス メソッドが導入されています。 クラス メソッドは新しいキーワード __classmethod で宣言します。
ここに __classmethod 構文を使って前に示したコードを記述する方法を示します。
// クラス参照は TMetaClass* をまた使います。
typedef TMetaClass* TTestClass;
class DELPHICLASS TTest;
class PASCALIMPLEMENTATION TTest : public System::TObject
{
typedef System::TObject inherited;
public:
// 今回 TMetaClass* パラメータは hidden です。
// __classmethod キーワードはメソッドがクラス メソッドであることを示すフラグです。
__classmethod int __fastcall add(int I, int J);
__classmethod UnicodeString __fastcall GetClsName();
// 仮想メソッドを使用できます。
__classmethod virtual UnicodeString __fastcall GetClsNameVirt();
// メソッドはクラス プロパティにアクセスできます。
__classmethod int __fastcall GetCount();
__classmethod void __fastcall SetCount(int I);
__classmethod virtual UnicodeString __fastcall GetStrProp();
__classmethod virtual void __fastcall SetStrProp(UnicodeString N);
// クラスの静的メソッドをまた C++ の 静的メソッドにマップします。
static int __fastcall GetstaticCount();
static void __fastcall SetstaticCount(int I);
// クラス プロパティ
__property int Count = {read=GetCount, write=SetCount, nodefault};
__property UnicodeString StrProp = {read=GetStrProp, write=SetStrProp};
};
Appmethod C++ には TMetaClass* 以外にクラス参照を宣言する方法がありません。 したがって、クラス メソッドは型名またはオブジェクトのインスタンスで呼び出されます。 仮想クラス メソッドは通常の仮想メソッドの呼び出しと同様に呼び出します。ただし、例外はこのポインタがプッシュされるのではなく、メタクラス ポインタは hidden パラメータであることです。
2 種類の機能が実装されています。
- クラス メソッドで仮想メソッドを使用できる。
- クラス プロパティを定義できる。
仮想クラス メソッドの動的ディスパッチ
別のクラスから派生したクラスに __classmethod を使ってクラス メソッドを定義した場合は、クラス名で派生クラスの仮想メソッドを動的に呼び出すことができません。 ただし、インスタンスを使って正しいメソッドを呼び出すことができます。
クラス メソッドの "仮想" メカニズムは通常のメソッドに対する仮想と同様です。 通常のメソッドの場合は、ポインタまたは参照を使用したときだけポリモーフィズムを利用できます。vtable が実行時に決定されるからです。 値のインスタンスでは、vtable がコンパイル時に決定されるので、ポリモーフィズムが利用できません。
同様に、クラス メソッドでは、インスタンスではポリモーフィズムが利用できますが、型ではできません。 次の例で説明します。
class TBase {
virtual __classmethod void cfunc();
virtual void vfunc();
};
class TDerived: TBase {
virtual __classmethod void cfunc();
virtual void vfunc();
};
// 通常の仮想メソッド
TDerived d;
d.vfunc(); // TDerived::vfunc; の呼び出し
TBase* bp = new TDerived();
bp->vfunc(); // TDerived::vfunc; の呼び出し
TBase& br = TDerived();
br.vfunc(); // TDerived::vfunc; の呼び出し
TBase b;
b.vfunc(); // TBase::vfunc の呼び出し
// クラス仮想メソッド
TBase* b = new TDerived();
b->cfunc(); // TDerived のバージョンの呼び出し(動的に決定)
TDerived::cfunc(); // TDerived のバージョンの呼び出し(動的ではなく、コンパイル時決定)
__classid(TDerived)->cfunc(); // コンパイラ エラー
__classmethod を使ったコードの書き換え
Metaclass * パラメータのある、古い形式のクラス メソッドを使用したクラス メソッドを C++ のコードに実装する場合は、__classmethod 構文でコードを書き直すことができます。 TMetaClass* パラメータが hidden なので、このようなクラス メソッドのシグネチャと名前変換は異なります。 次の表ではクラス メソッドを使って共通の C++ 構造を更新する方法について説明します。 add(int i, int j) メソッドと Count プロパティを前に示した __classmethod コード サンプルから使い、必要な変更を示します。
コードの目的 | 古い形式の「クラス static」コード | __classmethod を使って書き換えたコード |
---|---|---|
関数宣言 |
class TTest : public System::TObject { public: static int __fastcall add(TMetaClass* vmt, int I, int J); }; |
class TTest : public System::TObject { public: __classmethod int __fastcall add(int I, int J); }; |
__classid の使用 |
TTest::add(__classid(TTest), 10, 20); |
TTest::add(10, 20); |
派生した __classid の使用 |
TTestDerived::add(__classid(TTestDerived), 10, 20); |
TTestDerived::add(10, 20); |
クラス インスタンスの使用 |
TTest* p = new TTest(); ... TTest::add(p->ClassType(), 10, 20); |
TTest* p = new TTest(); ... p->add(10, 20); |
派生したインスタンスの使用 |
TTest* p = new TTestDerived(); ... TTest::add(p->ClassType(), 10, 20); |
TTest* p = new TTestDerived(); ... p->add(10, 20); |
クラス プロパティの使用 |
TTest* p = new TTest(); ... p->Count = 20; |
TTest::Count = 20; // クラス名を使用 // インスタンスを使用 TTest* p = new TTest(); ... p->Count = 20; |