デルファイの多型の分析
1多型とは何ですか? 2
1.1コンセプト2
1.2多型の意味2
1.3デルファイに多型を実装する方法は? 2
1.3.1継承2
1.3.2仮想メソッド、動的方法と抽象的な方法、VMT/DMT、静的バインディングおよび動的結合2
1.3.3過負荷と多型2
1.4多型種に関する議論2
1.4.1 2レベルの多型2
1.4.2安全でない多型2
2 VCL2の多型の応用
2.1構造と破壊方法2
2.2 TStrings2
2.3その他(魂を追加してください)2
抽象的な多型は、オブジェクト指向の魂です。
キーワード多型、相続、オブジェクト指向、VCL、仮想方法、オーバーライド
質問
多型はオブジェクト指向の魂であり、理解を理解することは、オブジェクト指向のテクノロジーを習得するための鍵の1つです。しかし、多型とは何ですか?多型の意味は何ですか?多型を達成する方法は?私は多型の概念を理解できますが、それを使用する方法とそれをいつ使用するかわかりませんか?この記事を詳細にお読みください。
専門家分析
世界(オブジェクト、つまりオブジェクト)は絶えず変化していますが、コンピューターの世界では、マシンの命令は1つしかありません。コンピューター言語では、一部の人々はc言語でオブジェクト指向のプログラムを書いていますが、執筆方法は非常に退屈であると結論付けています。ソフトウェア業界全体は、プログラミング言語の変更、SmallTalk、C ++、Java、Object Pascal、C#などの変化から変更されました。 VC、Delphi、BCBなどの指向的な開発ツールも登場しました。指向設計(OOD)、オブジェクト指向分析(OOA)、およびオブジェクト指向データベース(OODB))、オブジェクト指向テクノロジーはソフトウェアフィールド全体にほぼ侵入し、プログラマーの考え方も根本的な変化を遂げました!いくつかのOO浄化理論家の目には、すべてがオブジェクトです!私はこの見解には完全に同意しませんが。しかし、この方法は、人々の思考の習慣に最も沿っていると思いますそれ以降、私は解放されました!これは革命です!
オブジェクト指向のコンテンツは、オブジェクト、カプセル化、継承、多型、メッセージメカニズムですマスター多型、あなたは実際にオブジェクト指向のテクノロジーをマスターすることはありません。
1多型とは何ですか?
1.1コンセプト
多型の概念には多くの異なる意見があり、以下にはいくつかの代表的な声明があります。
「ポインターまたは基本クラスへの参照で複数のタイプを操作するこの能力は、多型として語っています」(C ++プライマー、838ページ)。つまり、基本クラスへのポインター/参照を使用して、複数のクラス(基本クラスとその派生クラス)のオブジェクトを操作する機能は、多型と呼ばれます。言語の実装の観点から考慮されます。
「多型は、インターフェースの分離の別の次元を実装から提供します。 「「別々」のスケールを行う方法。デザインの観点から考慮されます。
「同じ式を使用して異なる操作を示す能力は、多型と呼ばれます」(オブジェクト指向の方法原理&プラクティス3rd Edition、p。16)。簡単に言えば、多型は「同じ表現、異なる操作」であるか、「同じコマンド、異なる操作」と言うことができます。これは、オブジェクト指向のセマンティクスの観点からです。
3つの声明は、異なる視点からの多型の本質を説明しています。 3番目のステートメントは特に正確です。
最初にこの文の意味を説明させてください:
同じ式 - 機能コール
異なる操作 - 異なるオブジェクトに応じて異なる操作があります。
たとえば、会社には、「仕事に行く」ときに異なることをする責任(プログラマー、セールスマン、ドキュメントマネージャーなど)を持つさまざまな従業員がいます(一種のビジネスロジックとも見なすことができます)。それぞれの作品を「仕事に」として抽象化し、関係は次のとおりです。
スタッフ
/ |
プログラマーセールスマンドキュメントマネージャー
毎日勤務時間が来たら、次のようなコマンドを発行することに相当します。
「従業員。働き始める」(同じ表現)
各従業員がこのコマンド(同じコマンド)を受け取った後、彼は「働き始めます」が、彼らがするのはそれぞれの仕事であり、プログラマーは「コーディング」を開始し、営業担当者は「連絡先ビジネス」を開始し、文化マネージャーLet's Start'sを開始します。ドキュメントの整理」。つまり、「同じ式(関数呼び出し)、異なる操作(実行時間中の異なるオブジェクトに従って実行)。」
多型の言語実装の観点から、ポリモーフィズムは、基本クラスのポインターまたは派生クラスを指すオブジェクトへの参照によって仮想方法を呼び出すことによって達成されます。以下は、オブジェクトパスカル言語の実装です
Temployee = class //抽象従業員は抽象クラスになります
公共
手順StartWorking; virtual; abstract;
{要約関数(つまり、C ++の純粋な仮想関数)、何もしないでください。実際の意味は、最初にインターフェイスを予約することです。過負荷は、派生したクラスでそれを実装します。 }
終わり;
tprogramer = class(temployee)//プログラマー
公共
手順の起動。
終わり;
tbusinessman = class(temployee)//営業担当者
公共
手順の起動。
終わり;
tdocmanager = class(temployee)//テキストマネージャー
公共
手順の起動。
終わり;
手順tprogramer.startworking;
始める
showmessage( 'coding');
終わり;
{tbusinessman}
手順tbusinessman.startworking;
始める
showmessage( 'linking business');
終わり;
{tdocmanager}
手順tdocmanager.startworking;
始める
showmessage( 'Managing Document');
終わり;
手順tform1.button1click(sender:tobject);
const
enum = 3;
var
従業員:Temployeeの配列。
I:整数;
始める
setLength(従業員、列挙);
従業員[0]:= tprogramer.create;
//作成されたばかりのTprogramerオブジェクトを指すための基本クラスの従業員[0]を参照してください
従業員[1]:= tbusinessman.create;
//ベースクラスの従業員[1]を参照して、作成したばかりのTbusinessmanオブジェクトを指す
従業員[2]:= tdocmanager.create;
//作成されたばかりのTDOCMANAGERオブジェクトを指すための基本クラスの従業員[2]を参照してください
i:= 0から長さ(従業員)-1
従業員[i] .StartWorking;
{言語の実装多型の観点から、多型は、ベースクラスのポインターまたは派生クラスを指す参照オブジェクトによって仮想方法を呼び出すことにより実装されます。従業員[]は、基本クラスのオブジェクトの配列を指し、そのメンバーはそれぞれ異なるクラスオブジェクトを呼び出すとき、多型が実装されます}。
終わり;
試してみてください
上記のコード(またはデモプログラム)の一部を入力してコンパイルして実行し、ボタンをクリックして多型の魔法効果を確認できます。
1.2多型の意味
カプセル化と継承の意味は、彼らがコードの再利用を実装することですが、ポリモーフィズムの意味は、インターフェイスの再利用の利点を実装することです。有能です。
たとえば、より適切に管理するために、プログラマーはC ++プログラマーとDelphiプログラマーに分割されます。 …
スタッフ
/ |
プログラマーセールスマンドキュメントマネージャー
/ /——到達関係
C ++プログラマーDelphiプログラマー
プログラマーがTCPPProgramerとTDELPhiprogramerの2つの派生クラスを追加した後、コールメソッドはまだ変更されず、まだ「従業員」でした。オブジェクトPascalで説明されていました。
…
setLength(従業員、enum+2);
従業員[enum]:= tcppprogramer.create;
// tcppprogramerオブジェクトを作成し、基本クラスの参照従業員[列挙]を指します[列挙]
従業員[enum+1]:= tdelphiprogramer.create;
…
{セクターが機能し始めます}
i:= 0から長さ(従業員)-1
従業員[i] .StartWorking;
…
1.3デルファイに多型を実装する方法は?
多型を達成するために必要な条件は、継承、仮想的な方法、ダイナミックな結合(またはラグバック結合)です。
1.3.1継承
継承とは、プログラマーなどのクラス間の「Ako(A Stems of and a)」の関係を指し、「従業員」であり、相続関係を示しています。 Delphiでは、単一の継承のみがサポートされています(インターフェイスによって実装された多重継承を考慮していませんが、これは多重継承の柔軟性がありません。派生したクラスオブジェクト(またはその逆)。
ヒント
UMLで:
Ako:一種の手段相続関係
APO:の一部は、組み合わせ関係を表します
ISA:ISAは、オブジェクトとそれが属するクラスとの関係を表しています。
1.3.2仮想方法、動的方法と抽象的な方法、VMT/DMT、静的結合および動的結合
すべての方法について、オブジェクトにトレースはありません。そのメソッドポインター(エントリアドレス)はクラスに保存され、実際のコードはコードセグメントに保存されます。静的メソッド(非仮想的な方法)の場合、コンパイラは、コンパイルバインディングと呼ばれるコンパイル時間でのオブジェクトの参照タイプに基づいて、オブジェクトメソッドのエントリアドレスを直接決定します。コンパイラは、それが属する実際のクラスを決定することができないため、メソッドのエントリアドレスは、ランタイム中にVMTテーブルエントリアドレス(つまり、オブジェクトの最初の4バイト)を介してのみ決定できますラグバンドリング)。
仮想方法
仮想メソッドは、抽象的なメソッドとして宣言されていない場合、ベースクラスでデフォルトの実装が必要です。独自の仮想メソッドポインターの保存に加えて、クラスはすべてのベースクラスの仮想メソッドポインターも保存します。
宣言方法:
プロシージャメソッド名;
これは、Delphiコンパイラに次のように伝えることに相当します。
この方法は、派生クラスで過負荷にすることができ、仮想メソッドはまだ過負荷後になります。
コンピレーション期間中にメソッドのエントリアドレスを決定しないでください。ランタイム中に、メソッドのエントリアドレスは動的結合によって決定されます。
基本クラスでデフォルトの実装を提供します。
動的方法
動的な方法と仮想メソッドは、仮想メソッドとは異なり、クラスに独自の動的メソッドポインターのみを保存しますが、それらはより速く実行されます。しかし、これはユーザーに対して完全に透明です。
宣言方法:
手順名;動的;
抽象的なメソッド
基本クラスでデフォルトの実装を提供する必要のない特別な仮想方法は、C ++の純粋な仮想関数に相当する呼び出しインターフェイスにのみ使用されます。抽象的なメソッドを含むクラスは、抽象クラスと呼ばれます。
宣言方法:
手順名;仮想;要約;
VMT/DMT
Delphiでは、Virtual Method Table(VMT)は、実際には多型をよりよく説明するためではありません。基本クラスの仮想方法。オブジェクトの最初の4バイトに保存されている「VMTエントリアドレス」は、実際にはそれが属するクラスのアドレスです(デモプログラムを参照)。実際のクラスでは、仮想メソッドアドレスを見つけることができます。
obj(オブジェクト名)実際のオブジェクトが属するクラス
VMTエントリアドレス
データメンバー
クラス仮想メソッドテーブルのVMTエントリアドレス
データメンバーテンプレート情報
静的方法など
仮想メソッド(VMT)
動的方法(DMT)
図3オブジェクト名、オブジェクト、クラスの関係
DMTはVMTに似ており、違いはクラスの動的なメソッドポインターのみが保存されることですが、ベースクラスの動的方法のアドレスはありません。仮想的な方法ほど速くはありません。
説明するために上記の例を引用してください:
従業員[i] .StartWorking;
従業員[i]は、上記のプログラムが知っているように、トログラーのオブジェクトを指しているか、他のオブジェクトである可能性があります。したがって、実際のオブジェクトはコンピレーション中にわからないため、メソッドアドレスを決定することはできません。もちろん、実行期間中、私はオブジェクトの「Lushanの真の顔」、つまり仮想メソッドテーブルVMTのエントリアドレスに基づいています。呼び出される機能、つまり、多型が実現されます。
1.3.3過負荷と多型
多くのネチズンは、関数の過負荷も一種の多型であると考えていますが、そうではありません。 「異なる操作」の場合、オーバーロードは同じコールメソッドを提供できませんが、そのパラメーターは異なります。多型の実装の前提は同じ表現です!たとえば、従業員[i]。過負荷は単なる言語メカニズムであり、Cにも過負荷がありますが、Cには多型がなく、Cはオブジェクト指向のプログラミング言語ではありません。オーバーロード関数も仮想メソッドでない限り、コンパイラはパラメータータイプまたは静的バインディングに基づいて関数のエントリアドレスを決定できます。 C ++の父親を引用して、「愚かにならないでください。動的な結合でなければ、それは多型ではありません。」
1.4多型種に関する議論
1.4.1 2レベルの多型
オブジェクトレベル:ベースクラスのポインター/参照を使用して、その派生クラスオブジェクトを指し、最も一般的に使用されるものである仮想メソッド(または動的メソッド、抽象的方法)を呼び出します。
クラスレベル:クラス参照(オブジェクトではなくクラスへの参照)を使用して、派生クラスを指し、仮想クラスメソッド(または動的クラスメソッド、抽象クラスメソッド)を呼び出し、オブジェクトによって作成された多型で一般的に使用されます(コンストラクターは一種の「特別な」タイプの方法では、私の他の作業「デルファイの構造と破壊の分析」、セクション2.1を参照してください。
ヒント
クラス参照は、クラスではなく、クラス自体の参照変数でも、オブジェクト参照でもありません。オブジェクト名がオブジェクト参照を表すのと同様に、クラス名はクラス参照を表します。なぜなら、Delphiではクラスもオブジェクトとして処理されるためです。クラス参照タイプは、クラス参照のタイプであり、クラス参照タイプの宣言方法は次のとおりです。
クラス参照タイプ名=クラス名のクラス
次のような、VCLソースコードに多くのクラス参照宣言を見ることができます。
tclass = tobjectのクラス;
tcomponentClass =クラスのtcomponent;
tcontrolclass = tcontrolのクラス;
知らせ
クラスメソッドでは、メソッドの自己暗黙はクラス参照であり、オブジェクト参照ではありません。
1.4.2危険な多型
派生したクラスオブジェクトへの派生したクラスポインター/参照を使用して、多型を実装することもできます。
手順tform1.btnbadpolyclick(sender:tobject);
var
cppprogramer:tcppprogramer; //派生クラスへの参照であるCPPプログラマーリファレンスを定義します!
始める
{*****************************声明******************* ****************
派生クラスのポインター/基本クラスオブジェクトへの参照で実装された多型。それは病理学的多型です!
この多型の方法は、強力なもので覆われた非常に小さなもの(基本クラスのオブジェクト)のようなものです
したがって、製品の外観(派生クラス参照)は、多くの潜在的な不安要因(アクセスの例外など)をもたらすため、
ほとんど価値がありません。 「ファイル」の例は、デルファイの多型の性質、多型の性質を説明することを目的としています。ランタイム中の実際のオブジェクトに従って、異なる操作を実行するために、合法(通常は基本クラス)ポインター/参照を使用してオブジェクトを操作することを目的としています。方法、またはより鮮明なステートメント:オブジェクト自体は、独自の操作方法を決定する必要があります。オブジェクト。これにより、インターフェイスと実装の分離が可能になり、インターフェイスの再利用が可能になります。
******************************************** ******* ***************************}
cppprogramer:= tcppprogramer(tprogramer.create);
{この病理学的多型を達成するために、オブジェクト参照はTCPPProgramerタイプにキャストされます、
したがって、コンパイラのチェックを逃れます}
cppprogramer.startworking;
{tprogramer.startworking tcppprogramer.startworkingの代わりに呼び出します
これは、基本クラスオブジェクトへの派生クラスポインター/参照で実装された多型です。 }
cppprogramer.free;
cppprogramer:= tcppprogramer(tdocmanager.create);
cppprogramer.startworking;
{呼び出しはtdocmanager.startworkingです。
これは、基本クラスオブジェクトへの派生クラスポインター/参照で実装された多型です。この方法は非常に安全ではありません。
そして、必要はありません}
cppprogramer.free;
終わり;
試してみてください
この種の知覚的理解を得るために、この使用方法は潜在的な不安定なもの(アクセスの例外など)を試すことをお勧めします?アクセス例外がどのような状況で発生しますか? (デモプログラムを参照)
2 VCLの多型の応用
2.1構造と破壊方法
建設方法の多型
コンストラクターは「特別な」クラスメソッドと見なすことができるため、Tomponentの後にすべての派生クラスが仮想クラスメソッドとして再定義されるため、コンストラクターの多型を実現するには、クラス参照を使用する必要があります。各プロジェクトファイルには、次のようなコードがあります。
application.createform(tform1、form1);
その方法の定義:
手順tapplication.createform(instanceclass:tcomponentclass; var reference);
var // instanceclassはクラスの参照です。
インスタンス:tcomponent;
始める
インスタンス:= tcomponent(instanceclass.newinstance);
{newinstance宣言:newimstance:virtual; InstanceClassは、クラスレベルの多型を実装するクラスリファレンスであり、コンポーネントを作成するためのインターフェイスの再利用を実現します}
tcomponent(参照):= instance;
試す
instance.create(self); //コンストラクターを呼び出して初期化します
を除外する
tcomponent(参照):= nil; //「ワイルド」ポインターを排除します!良い
上げる;
終わり;
{作成されたウィンドウがまだメインフォームがない場合、作成されたフォームをメインフォームとして設定します}
if(fmainform = nil)および(instance is tform)then
始める
tform(instance).handleneeded;
fmainform:= tform(instance); //メインフォームを設定します
{実際、プロジェクトオプション(プロジェクト - >オプション)にメインフォームを設定することは、実際にはすべてのフォーム作成ステートメントの前にプロジェクトファイルの対応するフォームステートメントです。 }
終わり;
終わり;
2)破壊方法の多型については、「デルファイの構造と破壊の分析」、セクション3.3を参照してください。
2.2チストリングス
String配列処理は、Delphiの属性を使用するのに特に便利なDelphiコントロールで非常に一般的です。これは、Delphiの文字列アレイアーキテクチャのデザインのおかげで、使用するのに特に便利です(すべて同じインターフェイスを使用するため)。これは成功したデザインです。
多くのコントロールは、コンボボックス、TstringGridなどの文字列アレイを使用しているため、各コントロールの文字列配列は異なるため、Delphiは文字列アレイを抽象化するため、多くの関連クラスが表示されます。ベースクラスのtstringsは、さまざまな呼び出しのインターフェイスのみを提供し、特定の実装は派生クラスから完全に実装できます。
ベースクラスTStringsクラスの一般的な方法の定義を見てみましょう(クラスユニットライン442を参照)。
tstrings = class(tpersistent)
保護されています
...
関数(index:integer):birtual;
手順(index:integer; const s:string);
関数getCount:integer;
…
公共
function add(const s:string):virtual;
{文字列リストの最後に文字列sを追加}
手順ADDSTRINGS(文字列:TSTRINGS);
{文字列リストの最後に文字列を追加}
手順挿入(indeger; const s:string);
{要約方法、インデックス位置に新しい文字列sを挿入}
プロシージャクリア;
{すべての文字列をクリア}
手順削除(インデックス:整数);
{特定の場所で文字列を削除}
function indexof(const s:string):integer;
{文字列リストにSの位置を取得}
function indexofname(const name:string):integer;
{最初の文字列の位置をフォームname = valueで指定された名前パーツで返します}
関数indexofobject(aobject:tobject):integer;
{aobjectという名前のオブジェクトを使用してオブジェクトの位置を取得します:文字列リスト}
手順loadfromfile(const filename:string);
{指定されたファイルのテキストの行でリストに記入}
手順LoadFromStream(Stream:TStream);
{リストにテキストの行を記入して、ストリームから読みます}
手順savetostream(stream:tstream);
{テキストプロパティの値をストリームオブジェクトに書き込みます}
プロパティ文字列[index:integer]:文字列read get write put;
{リスト内の文字列を位置で参照}
プロパティ値[const name:string]:string read getvalue write setValue;
{特定の名前に関連付けられた文字列の値部分を表します。
…
終わり;
tstringsの定義から、その保護された方法とパブリック方法のほとんどが仮想的な方法または抽象的な方法であることがわかります。 (いくつかを追加してください、tstringlist-> tstringgridstring)
2.3その他(魂を追加してください)
あなたがまだ多型を理解していないなら、多型の本質を覚えておいてください:
「同じ表現、異なる操作」(それはそんなに単純です)
OOP言語の実装の観点から、多型はベースクラスのポインター/参照を使用して、実行期間中または変更中に実際のオブジェクトに従って異なる操作方法を実行することですより鮮明な声明:オブジェクトは、独自の操作方法を決定します自体。これにより、インターフェイスと実装の分離が可能になり、インターフェイスの再利用が可能になります。
実際、多型は簡単です!では、多型を使用するときに何に注意すべきでしょうか?これが私の2つの提案です:
ビジネスロジックを分析し、関連するものを「オブジェクト」に抽象化し、オブジェクトメソッドを使用してビジネスロジックをカプセル化します。いくつかの操作を基本クラスの仮想メソッドとして宣言し、基本クラスで実装するために必要ではないものの仮想抽象的なメソッドとして宣言し、派生クラス(オーバーライド)で呼び出されます/使用時のベースクラスのポインター。これは、現実世界の多型を自然に実現します。多型のために多型を達成しないことを忘れないでください。これは正式なアプローチであり、意味がありません。
基本クラスと派生クラスの間には自然な「結合」関係があるため、基本クラスを変更すると「全身に到達する」ことにつながります。したがって、基本クラスの機能的実装を弱め、必要に応じて「抽象クラス」として設計し、冗長な仮想関数(または抽象関数)を予約することで実現できるようにしなければなりません。
関連する質問
Delphiの多型について説明します:http://www.delphhibbs.com/delphhibbs/dispq.asp?lid=1753965
多型について:http://www.delphhibbs.com/delphhibbs/dispq.asp?lid=1854895
多型とは何ですか?毎日のプログラミングの用途は何ですか? http://www.delphibbs.com/delphibbs/dispq.asp?lid=960465
オーバーロードとオーバーライドの違いは何ですか? http://www.delphibs.com/delphibs/dispq.asp?lid=296739
派生クラスのポインターが基本クラスオブジェクトhttp://www.delphibs.com/delphibs/dispq.asp?lid=2104106を指していることを発行してください
(最後の質問は、私が多型を深く研究していたときにDelphibbsで言及されました。