ダイナミックディスパッチ

ウィキペディアから、無料の百科事典
ナビゲーションにジャンプ 検索にジャンプ

コンピュータサイエンスでは動的ディスパッチは、実行時に呼び出すポリモーフィック操作(メソッドまたは関数)の実装を選択するプロセスですこれは、オブジェクト指向プログラミング(OOP)言語およびシステムで一般的に使用されており、その主要な特性と見なされています。[1]

オブジェクト指向システムは、名前で参照される操作を実行する一連の相互作用するオブジェクトとして問題をモデル化します。ポリモーフィズムとは、ある程度互換性のあるオブジェクトがそれぞれ同じ名前の操作を公開するが、動作が異なる可能性がある現象です。例として、FileオブジェクトとDatabaseオブジェクトの両方に、人事レコードをストレージに書き込むために使用できるStoreRecordメソッドがあります。それらの実装は異なります。プログラムは、FileオブジェクトまたはDatabaseオブジェクトのいずれかであるオブジェクトへの参照を保持します。それが実行時の設定によって決定された可能性があり、この段階では、プログラムはどちらを認識または気にしない可能性があります。プログラムがStoreRecordを呼び出すときオブジェクトでは、どの動作を実行するかを選択する必要があります。OOPをオブジェクトへのメッセージの送信と考える場合、この例では、プログラムはStoreRecordメッセージを不明なタイプのオブジェクトに送信し、ランタイムサポートシステムにメッセージを送信して適切なオブジェクトに送信します。オブジェクトは、実装する動作を実行します。[2]

動的ディスパッチは、コンパイル時にポリモーフィック操作の実装が選択される静的ディスパッチとは対照的です。動的ディスパッチの目的は、パラメーター(または複数のパラメーター)の実行時タイプがわかるまで、適切な実装の選択を延期することです。

動的ディスパッチは、遅延バインディング(動的バインディングとも呼ばれます)とは異なります。名前バインディングは、名前を操作に関連付けます。ポリモーフィック操作にはいくつかの実装があり、すべて同じ名前に関連付けられています。バインディングは、コンパイル時または(遅延バインディングを使用して)実行時に行うことができます。動的ディスパッチでは、実行時に操作の1つの特定の実装が選択されます。動的ディスパッチは遅延バインディングを意味しませんが、遅延バインディング操作の実装は実行時までわからないため、遅延バインディングは動的ディスパッチを意味します。[要出典]

単一および複数のディスパッチ

呼び出すメソッドのバージョンの選択は、単一のオブジェクトまたはオブジェクトの組み合わせのいずれかに基づく場合があります。前者はシングルディスパッチと呼ばれ、 SmalltalkC ++JavaC#Objective-CSwiftJavaScriptPythonなどの一般的なオブジェクト指向言語で直接サポートされていますこれらの言語や同様の言語では、次のような構文で 除算のメソッドを呼び出すことができます。

配当除数除数  #被除数/除数

ここで、パラメーターはオプションです。これは、 divideという名前のメッセージをパラメータ除数dividendに送信すると考えられています。実装は、除数の型または値を無視して、被除数の型(おそらく有理数浮動小数点行列)のみに基づいて選択されます。

対照的に、一部の言語は、オペランドの組み合わせに基づいてメソッドまたは関数をディスパッチします。除算の場合、被除数除数のタイプが一緒になって、実行される除算演算が決まります。これは、多重ディスパッチとして知られています。多重ディスパッチをサポートする言語の例は、Common LispDylan、およびJuliaです。

動的ディスパッチメカニズム

言語は、さまざまな動的ディスパッチメカニズムで実装できます。言語によって提供される動的ディスパッチメカニズムの選択は、特定の言語内で使用可能な、または最も自然に使用できるプログラミングパラダイムを大幅に変更します。

通常、型付き言語では、ディスパッチメカニズムは引数のタイプに基づいて実行されます(最も一般的には、メッセージの受信者のタイプに基づいています)。タイピングシステムが弱い、またはまったくない言語では、各オブジェクトのオブジェクトデータの一部としてディスパッチテーブルが使用されることがよくあります。これにより、各インスタンスが特定のメッセージを個別のメソッドにマップする可能性があるため、 インスタンスの動作が可能になります。

一部の言語はハイブリッドアプローチを提供します。

動的ディスパッチは常にオーバーヘッドが発生するため、一部の言語では特定のメソッドに対して静的ディスパッチが提供されます。

C ++の実装

C ++は早期バインディングを使用し、動的ディスパッチと静的ディスパッチの両方を提供します。ディスパッチのデフォルト形式は静的です。動的ディスパッチを取得するには、プログラマーはメソッドを仮想として宣言する必要があります

C ++コンパイラは通常、仮想関数テーブル(vtable)と呼ばれるデータ構造を使用して動的ディスパッチを実装します。このデータ構造は、特定のクラスの名前から実装へのマッピングをメンバー関数ポインタのセットとして定義します。(これは純粋に実装の詳細です。C++仕様ではvtablesについては言及されていません。)そのタイプのインスタンスは、インスタンスデータの一部としてこのテーブルへのポインターを格納します。多重継承が使用される場合、これは複雑です。C ++は遅延バインディングをサポートしていないため、C ++オブジェクトの仮想テーブルは実行時に変更できません。これにより、ディスパッチターゲットの潜在的なセットがコンパイル時に選択された有限のセットに制限されます。

言語は正式なメッセージ名の一部であるメッセージパラメータのタイプを考慮するため、タイプのオーバーロードはC ++で動的ディスパッチを生成しません。これは、プログラマーに表示されるメッセージ名が、バインディングに使用される正式な名前ではないことを意味します。

Go andRustの実装

Go and Rustでは、初期バインディングのより用途の広いバリエーションが使用されます。 Vtableポインターは、オブジェクト参照とともに「ファットポインター」(Goでは「interfaces」、Rustでは「traitobjects」)として運ばれます。

これにより、サポートされているインターフェイスが基盤となるデータ構造から切り離されます。コンパイルされた各ライブラリは、タイプを正しく使用するためにサポートされているインターフェイスの全範囲を知る必要はなく、必要な特定のvtableレイアウトだけを知る必要があります。コードは、同じデータへのさまざまなインターフェイスをさまざまな機能に渡すことができます。この汎用性は、各オブジェクト参照に追加のデータを犠牲にしてもたらされます。これは、そのような参照の多くが永続的に格納されている場合に問題になります。

ファットポインターという用語は、単に追加の関連情報を持つポインターを指します。追加情報は、上記の動的ディスパッチのvtableポインターである場合がありますが、より一般的には、スライスなどを記述するための関連オブジェクトのサイズです。

Smalltalkの実装

Smalltalkは、タイプベースのメッセージディスパッチャを使用します。各インスタンスには、定義にメソッドが含まれる単一のタイプがあります。インスタンスがメッセージを受信すると、ディスパッチャはメッセージからメソッドへのマップでタイプの対応するメソッドを検索し、メソッドを呼び出します。

タイプは基本タイプのチェーンを持つことができるため、このルックアップはコストがかかる可能性があります。Smalltalkのメカニズムの単純な実装は、C ++の実装よりも大幅に高いオーバーヘッドがあるように見え、このオーバーヘッドは、オブジェクトが受信するすべてのメッセージに対して発生します。

実際のSmalltalkの実装では、多くの場合、インラインキャッシングと呼ばれる手法が使用されます[3]。これにより、メソッドのディスパッチが非常に高速になります。インラインキャッシングは基本的に、コールサイトの以前の宛先メソッドアドレスとオブジェクトクラス(またはマルチウェイキャッシングの場合は複数のペア)を格納します。キャッシュされたメソッドは、メソッドセレクターに基づいて、最も一般的なターゲットメソッド(または単にキャッシュミスハンドラー)で初期化されます。実行中にメソッド呼び出しサイトに到達すると、キャッシュ内のアドレスを呼び出すだけです。(動的コードジェネレーターでは、直接アドレスはキャッシュミスロジックによってバックパッチされるため、この呼び出しは直接呼び出しです。)呼び出されたメソッドのプロローグコードは、キャッシュされたクラスを実際のオブジェクトクラスと比較し、それらが一致しない場合、実行はキャッシュミスハンドラーに分岐して、クラス内の正しいメソッドを見つけます。高速な実装には複数のキャッシュエントリが含まれる場合があり、最初のキャッシュミスで正しいメソッドを実行するには、多くの場合、数命令しかかかりません。一般的なケースは、キャッシュされたクラスの一致であり、実行はメソッド内で続行されます。

アウトオブラインキャッシングは、オブジェクトクラスとメソッドセレクターを使用して、メソッド呼び出しロジックで使用することもできます。1つの設計では、クラスとメソッドセレクターがハッシュされ、メソッドディスパッチキャッシュテーブルへのインデックスとして使用されます。

Smalltalkはリフレクション言語であるため、多くの実装では、動的に生成されたメソッドルックアップテーブルを使用して、個々のオブジェクトをオブジェクトに変更できます。これにより、オブジェクトごとにオブジェクトの動作を変更できます。プロトタイプベースの言語として知られる言語のカテゴリ全体がこれから成長しました。その中で最も有名なのはSelfJavaScriptです。メソッドディスパッチキャッシングを注意深く設計することで、プロトタイプベースの言語でも高性能のメソッドディスパッチを実現できます。

PythonRubyObjective-CGroovyなど、他の多くの動的型付け言語も同様のアプローチを使用しています。

Pythonでの例

class  Cat 
    def  talk self ):
        print "Meow" 

class  Dog 
    def talk  self ):print ( " Woof " 
        


def talk  pet ): # speakメソッドを動的にディスパッチします#petはCatまたはDogpetのインスタンスのいずれかになります話す()
    
    
    

cat  =  Cat ()
speak cat 
dog  =  Dog ()
speak dog 

C ++での例

#include <iostream> 
名前空間stdを使用します;  

// Petを抽象仮想基本クラス
クラス Pet { 
    パブリック
    仮想ボイドスピーク()= 0 ;    
};

クラス パブリックペット{    
    パブリック
    ボイドスピーク() 
    {{
        std :: cout << "Woof!\ n " ;
    }
};

クラス パブリックペット{    
    パブリック
    ボイドスピーク() 
    {{
        std :: cout << "Meow!\ n " ;
    }
};


// Speakは、Pet void talk Pet pet から派生したものをすべて受け入れることができます  
{{
    ペット話す();
}

int main () 
{{
    犬のフィド; 
    シンバ; 
    話すfido );
    話すシンバ);
    0を返す; 
}

も参照してください

参考文献

  1. ^ ミルトン、スコット; シュミット、ハインツW.(1994)。オブジェクト指向言語での動的ディスパッチ(テクニカルレポート)。TR-CS-94-02。オーストラリア国立大学。CiteSeerX10.1.1.33.4292 _
  2. ^ ドライセン、カレル; Hölzle、Urs; Vitek、1月(1995)。「パイプラインプロセッサでのメッセージディスパッチ」。ECOOP'95 —オブジェクト指向プログラミング、第9回ヨーロッパ会議、デンマーク、オーフス、1995年8月7〜11日コンピュータサイエンスの講義ノート。952.スプリンガー。CiteSeerX10.1.1.122.281_ 土井10.1007 / 3-540-49538-X_13ISBN  3-540-49538-X
  3. ^ ミュラー、マーティン(1995)。動的型付けされたオブジェクト指向言語でのメッセージディスパッチ(修士論文)。ニューメキシコ大学。pp。16–17。CiteSeerX10.1.1.55.1782_ 

参考文献