最近、インターネット上でネチズンが「オブジェクト ポインタが利用可能かどうかを確認するにはどうすればよいですか?」と質問しているのを見かけました。言い換えれば、オブジェクト ポインターが実際に使用可能なオブジェクト インスタンスを指しているかどうかをどのように判断するかということです。実際、これは問題ないはずです。なぜなら、プログラマにとって、すべてのオブジェクト インスタンスの作成と破棄はプログラマの制御下にあるため、無効なポインタにアクセスしないようにプログラムを制御できる必要があるからです。また、オブジェクト ポインターが利用可能かどうかを直接判断する方法がない場合でも、他の間接的な方法 (識別子の使用など) を通じて判断することもできます (たとえば、オブジェクト インスタンスを破棄するときは、オブジェクトへのポインタのポインタが nil です)。しかし、上記の 2 つの点を横に置いて、Delphi でオブジェクト ポインターが使用可能かどうかを判断する方法があるかどうかだけを検討すると、何が起こるでしょうか。
Object Pascal では、クラスは 2 種類のメソッドを持つことができます。1 つはオブジェクト メソッド (Object Method) と呼ばれ、もう 1 つはクラス メソッド (Class Method) と呼ばれます。いわゆるオブジェクト メソッドは、メソッドの定義がオブジェクト (またはインスタンス) 用であることを意味するため、メソッドの呼び出しはオブジェクト (またはインスタンス) に基づいて行う必要があります。たとえば、クラスのデストラクター Destroy はオブジェクトです。メソッド (実際、使用されるメソッドのほとんどはオブジェクト メソッドであることがよくあります)。クラス メソッドは、オブジェクトのクラスに基づくメソッドの定義を参照するため、メソッドの呼び出しは、クラスのコンストラクター Create などの特定のオブジェクト インスタンスに基づく必要はありません。これは私たちにインスピレーションを与えてくれます。オブジェクト ポインタが使用可能かどうかの判断は、次の手順で行われるようです。まず、オブジェクト ポインタが nil かどうかを判断できます。そうであれば、作業は完了です。そうでない場合は、オブジェクトのオブジェクト メソッドを実行して、無効なメモリ アクセスなどの例外があるかどうかを確認します。これは、オブジェクトが利用可能かどうかを判断するために使用されます。次のコードを使用して、私たちのアイデアを検証します。
変数
オブジェクト: TObject;
始める
Obj := TObject.Create; //1. オブジェクトを作成します。
Obj.Free; //2. 作成したオブジェクトを解放すると、この時点でメモリがリサイクルされます。
Obj = nil の場合、 //3 ポインタが空かどうかを判断します (オブジェクトが空であるため、このステップは失敗することがよくあります)
// 解放されると、Delphi はオブジェクト ポインタを自動的に空にしません)
ShowMessage('オブジェクト ポインターが使用できません。')
それ以外
始める
試す
Obj.ClassType = TObject の場合、 //4. TObject のオブジェクト メソッドを呼び出します。
ShowMessage('オブジェクト タイプは TObject です');
を除外する
ShowMessage('オブジェクト ポインターが使用できません。')
終わり;
終わり;
終わり;
上記のコードを実行すると、Obj.Free が実行されていても、Obj.ClassType がまだ利用可能であることがわかります。これは、すべてのオブジェクト メソッドがアクセス可能になるためにオブジェクト インスタンスに依存する必要はないことを示しています。その理由は、このオブジェクト メソッドはオブジェクト インスタンスによって要求されたメモリにアクセスする必要がないためです。この意味で、TObject.ClassType メソッドは実際のオブジェクト メソッドのようには見えませんが、むしろクラス メソッドに似ています。
上記のコードを実行すると、オブジェクトが Free メソッドを実行すると、オブジェクトの作成時に適用されたすべてのメモリが解放されるだけで、オブジェクト ポインター自体の値には影響しないことがわかります。オブジェクト ポインタは依然として元のメモリ アドレスを指します。同時に、一部のオブジェクト メソッド (ClassType など) の実装の特殊性により、オブジェクトが解放された場合でも、オブジェクト メソッド呼び出しの結果は依然として正しいままです。
要約すると、オブジェクト ポインターが使用可能であると判断できるかどうかは、オブジェクト ポインターが属するクラスがオブジェクト インスタンス メモリにアクセスする方法を提供しているかどうかによって決まります。この方法は、メソッドによっても可能です。プロパティになります。では、それぞれのカテゴリーにおいて、現在は具体的にどのような状況になっているのでしょうか?
TObject、このクラスはすべてのクラスの祖先クラスであるため、判断する方法はありません。
TObject から派生した TPersistent は、オブジェクト インスタンスの作成時に追加のメモリを適用する必要がないため、判断する方法がありません。
TPersistent から派生した TComponent は、オブジェクト インスタンスの作成時に適用される追加メモリを必要とするプロパティを多数追加するため、理論上は判断されます。コードは次のとおりです。
関数 ComponentExists(AComponent: TComponent): ブール値;
始める
試す
AComponent.Hasparent; //注: この文は「AComponent.Tag;」でも構いません。
//または「AComponent.Name」
結果 := True;
を除外する
結果 := False;
終わり;
終わり;
ComponentExists を呼び出すと、オブジェクト ポインターが解放されているか nil に設定されているかに関係なく、TComponent 型のオブジェクト ポインターが利用可能かどうかを知ることができます。
その他のクラス (TControl、TWinControl、TButton など) についても、TComponent から派生している限り、TComponent の判定メソッドが適用されます。
他のユーザー定義クラスがあり、それらが判断できないクラス (TObject や TPersistent など) から直接派生している場合、インスタンス化時にメモリ アプリケーションを必要とする属性がない場合は、判断する方法がありません。可能です。一例によると:
次のように定義された TPersonal クラスがあるとします。
Tパーソン = クラス(Tオブジェクト)
プライベート
FSex: TSex; // TSex は列挙型の性別です。
F名: 文字列;
F姓: 文字列;
//…
公共
プロパティ性別: TSex 読み取り FSex 書き込み FSex;
property FirstName: 文字列 読み取り FFirstName 書き込み FFirstName;
property LastName: 文字列 読み取り FLastName 書き込み FLastName;
//…
終わり;
次に、Tperson 型のポインター person については、次のコードを使用して、ポインターが使用可能かどうかを判断できます。
試す
人物.性別;
//または Person.FirstName;
//または Person.LastName;
result := True //ポインタは使用可能です。
を除外する
result := False;//ポインタは使用できません
終わり;
上で説明したことは、単なる技術的な可能性です。私が強調したいのは、たとえ良い方法があったとしても、それを頻繁に行うことは推奨されないということです。厳密なロジックを備えたプログラムでは、無効なポインターへのアクセスを防止できる必要があるためです。
その他の記事