動的関数

提供: RAD Studio
移動先: 案内検索

仮想関数:インデックス への移動

動的関数は、System.TObject から派生したクラスに使用できます。動的関数は仮想関数と似ていますが、仮想テーブルに格納される方法が異なります。

  • 仮想関数は、それらが定義されているオブジェクトの仮想テーブルのスロットと、そのオブジェクトのあらゆる下位オブジェクトの仮想テーブルのスロットを占有します。
  • 動的関数は、それらを定義しているあらゆるオブジェクト内のスロットを占有しますが、下位オブジェクト内のスロットについては占有しません。

つまり、動的関数は、まばらに存在する仮想テーブルに格納された仮想関数です。動的関数を呼び出す場合、その関数がオブジェクトに定義されていなければ、関数が見つかるまで上位オブジェクトの仮想テーブルが検索されます。

したがって、動的関数の場合は、関数のアドレスを検索するために実行時に遅延が発生しますが、それと引き換えに、仮想テーブルのサイズが小さくなります。

動的関数は TObject から派生したクラスでのみ使用できるため、通常のクラスでそれらを使用すると、エラーが発生します。たとえば、

class dynfunc {
  int __declspec(dynamic) bar() { return 5; }
 };

上記のコードでは、"Error: Storage class 'dynamic' is not allowed here.(エラー: 記憶クラス 'dynamic' はここでは使用できません。)" という構文エラーが発生します。

一方、以下のコードはコンパイルされます。

 #include <System.hpp>
 #include <stdio.h>
 class __declspec(delphiclass) func1 : public TObject {
  public:
   func1() {}
   int virtual virtbar() { return 5; }
   int __declspec(dynamic) dynbar() { return 5; }
  };
 class __declspec(delphiclass) func2 : public func1 {
  public:
   func2() {}
   };
 class __declspec(delphiclass) func3 : public func2 {
  public:
   func3() {}
   int virtbar() { return 10; }
   int __declspec(dynamic) dynbar() { return 10; }
  };
 int main()
  {
   func3 * Func3 = new func3; func1 * Func1 = Func3;
   printf("func3->dynbar: %d\n", Func3->dynbar());
   printf("func3->virtbar: %d\n", Func3->virtbar());
   printf("func1->dynbar: %d\n", Func1->dynbar());
   printf("func1->virtbar: %d\n", Func1->virtbar());
   delete Func3; func2 * Func2 = new func2;
   printf("func2->dynbar: %d\n", Func2->dynbar());
   printf("func2->virtbar: %d\n", Func2->virtbar());
   delete Func2; return 0;
  }

dynamic 属性は継承される

一般に、動的関数は仮想関数同然で、dynamic 属性が自動的に継承されます。

ただし、基底型における動的メソッドと同じシグネチャのメソッドが派生型で宣言されている場合、そのメソッドには同じ動的 ID が割り当てられますが、派生型におけるメソッドでは __declspec(dynamic) と指定する必要があります。そうしないと、[BCC32 エラー] wibwob2.cpp(21): E2113 仮想関数 'func3::dynbar()' は基底クラス 'func1' と矛盾するというコンパイラ エラーが発生します。

このことは、上記の例を実行することで確かめることができます。"bcc32 -S" でアセンブリ出力を生成すると、func1、func2、func3 の仮想テーブルを調べることができ、その結果、func2 には dynbar のエントリがなく virtbar のエントリがあることを確認できます。それでも、func2 オブジェクトで dynbar を呼び出すことができます。

出力は以下のとおりです。

func3->dynbar: 10
func3->virtbar: 10
func1->dynbar: 10
func1->virtbar: 10
func2->dynbar: 5
func2->virtbar: 5

動的関数は仮想関数にはできず、仮想関数も動的関数にはできない

仮想関数を動的関数として再宣言することはできず、同様に、動的関数を仮想関数として再宣言することもできません。以下の例では、エラーが発生します。

 #include <vcl.h>;
  #include <stdio.h>;
  class __declspec(delphiclass) foo1 : public TObject {
   public:
    foo1() {}
    int virtual virtbar() { return 5; }
    int __declspec(dynamic) dynbar() { return 5; }
   };
 
  class __declspec(delphiclass) foo2 : public foo1 {
   public:
    foo2() {}
    int __declspec(dynamic) virtbar() { return 10; }
    int virtual dynbar() { return 10; }
   };
 
  Error : Cannot override a virtual with a dynamic function
 
  Error : Cannot override a dynamic with a virtual function

関連項目