10

Microsoft のGDI+は、内部でハンドルとして扱われる多くの空のクラスを定義します。たとえば、(ソースGdiPlusGpStubs.h

//Approach 1

class GpGraphics {};

class GpBrush {};
class GpTexture : public GpBrush {};
class GpSolidFill : public GpBrush {};
class GpLineGradient : public GpBrush {};
class GpPathGradient : public GpBrush {};
class GpHatch : public GpBrush {};

class GpPen {};
class GpCustomLineCap {};

ハンドルを定義するには、他に 2 つの方法があります。彼らは、

//Approach 2
class BOOK;  //no need to define it!
typedef BOOK *PBOOK;
typedef PBOOK HBOOK; //handle to be used internally

//Approach 3
typedef void* PVOID;
typedef PVOID HBOOK; //handle to be used internally

これらの各アプローチの長所と短所を知りたいだけです。

Microsoft のアプローチの利点の 1 つは、空のクラスを使用してハンドルのタイプ セーフな 階層を定義できることです。これは (私が思うに) 他の 2 つのアプローチでは不可能ですが、この階層が実装にどのような利点をもたらすのでしょうか? とにかく、他に何?

編集:

2 番目のアプローチ (つまり、不完全なクラスを使用する) の利点の 1 つは、クライアントがハンドルを逆参照するのを防ぐことができることです (つまり、このアプローチはカプセル化を強力にサポートしているように見えます)。ハンドルを逆参照しようとすると、コードはコンパイルされません。ほかに何か?

ハンドルを逆参照できないという 3 番目のアプローチにも同じ利点があります。

4

3 に答える 3

3

アプローチ #1 は、C スタイルと C++ インターフェイスの中間です。メンバー関数の代わりに、ハンドルを引数として渡す必要があります。公開ポリモーフィズムの利点は、インターフェイス内の関数の量を減らすことができ、型がコンパイル時にチェックされることです。通常、ほとんどの専門家は、そのようなインターフェイスよりも pimpl イディオム (コンパイル ファイアウォールと呼ばれることもあります) を好みます。アプローチ #1 を使用して C とやり取りすることはできないため、完全な C++ を使用することをお勧めします。

アプローチ #2 は、C スタイルのカプセル化と情報隠蔽です。ポインターは、実際のものへのポインターである可能性があり (多くの場合)、過度に設計されていません。ライブラリのユーザーは、そのポインターを逆参照できません。欠点は、ポリモーフィズムがまったく公開されないことです。利点は、C で記述されたモジュールとやり取りするときに使用できることです。

アプローチ #3 は、過度に抽象化された C スタイルのカプセル化です。ライブラリのユーザーはポインタをキャスト、割り当て解除、または逆参照してはならないため、ポインタは実際にはまったくポインタではない可能性があります。利点は、例外またはエラー値を運ぶ可能性があることです。欠点は、そのほとんどを実行時にチェックする必要があることです。

言語に中立なオブジェクト指向インターフェイスは C++ から使用するのが非常に簡単でエレガントであるという DeadMG の意見に同意しますが、コンパイル時のチェックよりも実行時のチェックが多く、他の言語とのインターフェイスが必要ない場合はやり過ぎです。したがって、C++のみの場合にCまたはPimplイディオムとインターフェースする必要がある場合は、アプローチ#2を個人的に好みます。

于 2010-12-24T18:43:08.660 に答える
2

アプローチ3は、実際には意味をなさないハンドルタイプの混合と一致を可能にするため、まったく良くありません.HANDLEを取る関数は、コンパイル時にそれが間違っていると判断できる場合でも、任意のHANDLEを取ることができます.タイプ。

アプローチ 1 の欠点は、実際の型に対して反対側で大量のキャストを行う必要があることです。

アプローチ 2 はそれほど悪くはありませんが、毎回外部からクエリを実行しなければ、いかなる種類の継承も行うことができません。

ただし、コンパイラが効率的な仮想関数を実装する方法を発見して以来、これはすべてまったく意味がありません。DirectX と COM のアプローチは最高で、非常に柔軟で強力で、完全にタイプ セーフです。

DirectX インターフェイスから継承し、そのように拡張できるなど、本当に非常識なことも可能です。これの最大の利点の 1 つは、Direct2D と Direct3D11 です。それらは実際には互換性がありません (これは本当に恐ろしくばかげています) が、ID3D10Device1 から継承して ID3D11Device に転送するプロキシ タイプを定義し、そのような問題を解決できます。そのようなことは、上記のアプローチのいずれかで可能になるとは考えもしません。

ああ、最後に: 型にすべて大文字で名前を付けるべきではありません。

于 2010-12-24T12:54:35.017 に答える
1

2 と 3 は、void* の代わりにハンドルを使用できるため、型安全性がわずかに劣ります。

ボイドブルースクリーン(HBOOK hb){
  memset(hb,0,100000); // コンパイル エラーなし
}
于 2010-12-24T12:37:05.670 に答える