クラス メソッド
クラス:インデックス への移動
C++ では、クラス メソッドとは、クラスのインスタンスとクラス名で呼び出すことができるメソッドです。 それに対して、オブジェクト メソッドはオブジェクトつまりクラスのインスタンスのみで呼び出すことができます。
目次
Delphi でのクラス メソッドの動作
Delphi 言語(Object Pascal)は Delphi ヘルプ トピック「メソッド(Delphi)」の「クラス メソッド」セクションに説明されているとおりにメタクラスとクラス メソッドをサポートします。
ここで例を示します。
TTest = class;
// Declaring metaclass type
TTestClass = class of TTest;
TTest = class
public
// Class methods
class function add(I, J: Integer): Integer;
class function GetClsName: string;
// Virtual class method
class function GetClsNameVirt: string; virtual;
// Class getter and setter
class function GetCount: Integer;
class procedure SetCount(I: Integer);
// Virtual class getter and setter
class function GetStrProp: string; virtual;
class procedure SetStrProp(N: string); virtual;
// Class static
class function GetStaticCount: Integer; static;
class procedure SetStaticCount(I: Integer); static;
// Class properties
property Count: Integer read GetCount write SetCount; // Non-virtual
property StrProp: string read GetStrProp write SetStrProp; // Virtual g/setters
end;
// Function that takes a class reference
function RegisterTestType(Cls: TTestClass): boolean;
明示的に TMetaClass* パラメータが指定された静的メソッドのクラス メソッド
C++Builder 2009 より前では、クラス メソッドは明示的にメタクラス パラメータが指定された静的メソッドとして表現されていました。 メタクラスは TClass または TMetaClass インスタンスへのポインタで表現されます。 このメタクラス(クラス参照)は __classid 拡張で取得され、クラス名の TMetaClass* インスタンスが返ります。
次に使用例を示します。Delphi の前の例と同じプロパティやメソッドの定義を試みます。 Delphi のクラス メソッド機能の一部はこのメタクラスのアプローチでは正しく実行できないことに注意してください。
// All metaclass types are TMetaClass* in C++
typedef TMetaClass* TTestClass;
class DELPHICLASS TTest;
class PASCALIMPLEMENTATION TTest : public System::TObject
{
typedef System::TObject inherited;
public:
// Class methods exposed as static methods with the 'hidden'
// class reference explicit as the first parameter.
static int __fastcall add(TMetaClass* vmt, int I, int J);
static UnicodeString __fastcall GetClsName(TMetaClass* vmt);
// Virtual class methods would be exposed as plain virtual methods with
// the hidden class reference explicit as the first parameter.
// This means that upon calling this method from C++, there would have
// to be two 'hidden' parameters passed in--which would not work.
virtual UnicodeString __fastcall GetClsNameVirt(TMetaClass* vmt);
// Non-virtual methods are feasible to work with. These two methods
// are typically overloaded with the first TMetaClass* parameter
// hardcoded to __classid(TTest).
static int __fastcall GetCount(TMetaClass* vmt);
static void __fastcall SetCount(TMetaClass* vmt, int I);
// You can overload these virtual setters and getters, but given
// that the call is incorrect, the program fails
// when accessing the property tied to these methods.
virtual UnicodeString __fastcall GetStrProp(TMetaClass* vmt);
virtual void __fastcall SetStrProp(TMetaClass* vmt, UnicodeString N);
// Delphi class static method would be plain C++ static.
static int __fastcall GetStaticCount();
static void __fastcall SetStaticCount(int I);
// Although the compiler allows these declarations,
// because TMetaClass* is required, you'll get an error
// from the C++ compiler upon attempting to access these properties.
__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 キーワードを使用したクラス メソッド
C++Builder 2009 では前に示した制限が取り除かれ、構文が簡単でさらに直感的になったクラス メソッドが導入されています。 クラス メソッドは新しいキーワード __classmethod で宣言します。
ここに __classmethod 構文を使って前に示したコードを記述する方法を示します。
// Class references still use TMetaClass*.
typedef TMetaClass* TTestClass;
class DELPHICLASS TTest;
class PASCALIMPLEMENTATION TTest : public System::TObject
{
typedef System::TObject inherited;
public:
// The TMetaClass* parameter is now hidden.
// The __classmethod keyword flags methods as class methods.
__classmethod int __fastcall add(int I, int J);
__classmethod UnicodeString __fastcall GetClsName();
// Virtual methods can be used.
__classmethod virtual UnicodeString __fastcall GetClsNameVirt();
// Methods can access class properties
__classmethod int __fastcall GetCount();
__classmethod void __fastcall SetCount(int I);
__classmethod virtual UnicodeString __fastcall GetStrProp();
__classmethod virtual void __fastcall SetStrProp(UnicodeString N);
// Class static methods still map to C++ static methods.
static int __fastcall GetstaticCount();
static void __fastcall SetstaticCount(int I);
// Class properties
__property int Count = {read=GetCount, write=SetCount, nodefault};
__property UnicodeString StrProp = {read=GetStrProp, write=SetStrProp};
};
C++Builder には 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();
};
// Regular virtual methods
TDerived d;
d.vfunc(); //calls TDerived::vfunc;
TBase* bp = new TDerived();
bp->vfunc(); //calls TDerived::vfunc;
TBase& br = TDerived();
br.vfunc(); //calls TDerived::vfunc;
TBase b;
b.vfunc(); //calls TBase::vfunc
// Class virtual methods
TBase* b = new TDerived();
b->cfunc(); //calls version in TDerived--dynamic
TDerived::cfunc(); //calls version in TDerived--compile time--not dynamic
__classid(TDerived)->cfunc(); //compiler error
__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; //Using class name
// Using instance
TTest* p = new TTest();
// …
p->Count = 20;
|