11

私はビジターパターンを次のように書きましたが、シングルディスパッチとダブルディスパッチが何であるかわかりません。AFAIK、シングルディスパッチは呼び出し元のタイプに基づいてメソッドを呼び出しますが、ダブルディスパッチは呼び出し元のタイプと引数のタイプに基づいてメソッドを呼び出します。

ダブルディスパッチはシングルクラス階層で発生すると思いますが、ビジタークラスに2つのクラス階層があるのに、それでもダブルディスパッチと見なされるのはなぜですか。

void floppyDisk::accept(equipmentVisitor* visitor)
{
 visitor->visitFloppyDisk(this);
}

void processor::accept(equipmentVisitor* visitor)
{
 visitor->visitProcessor(this);
}

void computer::accept(equipmentVisitor* visitor)
{
 BOOST_FOREACH(equipment* anEquip, cont)
 {
  anEquip->accept(visitor);
 }

 visitor->visitComputer(this);
}

void visitFloppyDisk(floppyDisk* );
void visitProcessor(processor* );
void visitComputer(computer* );

私が提供したサンプルコードを使用して説明してください。

AFAIK、最初のディスパッチはacceptを呼び出すオブジェクトで発生し、2番目のディスパッチはvisitメソッドを呼び出すオブジェクトで発生します。

ありがとう。

4

2 に答える 2

13

要するに、シングルディスパッチとは、メソッドが1つのパラメーターのタイプ(暗黙的を含むthis)で多態的である場合です。ダブルディスパッチは、2つのパラメーターのポリモーフィズムです。

最初の例の典型的な例は、標準仮想メソッドです。これは、含まれているオブジェクトの型にポリモーフィックです。そして2つ目はVisitorパターンを介して実装できます。

[更新]あなたの例ではfloppyDisk、、、processorおよびそれぞれが仮想メソッドとしてcomputer定義されている共通の基本クラスから継承していると思います。accept同様に、visit*メソッドはで仮想として宣言する必要があります。これには、実装equipmentVisitorが異なるいくつかの派生クラスが含まれている必要があります。[/アップデート]visit*

上記を仮定すると、との両方でaccept多形です。フロッピーディスク、プロセッサ、およびコンピュータにはそれぞれ独自の実装があるため、訪問者がを呼び出すと、呼び出し先のタイプに基づいてcalがディスパッチされます。次に、呼び出し先は訪問者のタイプ固有の訪問方法をコールバックし、この呼び出しは実際の訪問者のタイプに基づいてディスパッチされます。thisequipmentVisitoracceptaccept

理論的には、トリプル、クワッドなどのディスパッチもありますが、これが実際に実装されているのを見たことがありません(2倍以上のディスパッチを本質的にサポートしていない言語では、つまり、Smalltalkがサポートしていることを覚えているようです)。C ++および同様の言語でVisitorを使用したダブルディスパッチは、それ自体がすでに非常に気が遠くなるようなものであるため、トリプル以上のディスパッチの実装は、実際のアプリケーションで使用するには複雑すぎます。

于 2010-07-16T07:49:13.553 に答える
7

あなたの例では、メカニズムの基本である継承と仮想性が欠落しています。コードに加えて、次のクラス階層を想定しましょう。

class equipmentVisited
{
  virtual void accept(equipmentVisitor* visitor) = 0;
}

class floppyDisk : public equipmentVisited
{
  virtual void accept(equipmentVisitor* visitor);
}

class processor : public equipmentVisited
{
  virtual void accept(equipmentVisitor* visitor);
}

class computer : public equipmentVisited
{
  virtual void accept(equipmentVisitor* visitor);
}

class equipmentVisitor
{
  virtual void visitFloppyDisk(floppyDisk* );
  virtual void visitProcessor(processor* );
  virtual void visitComputer(computer* );
}

// Some additional classes inheriting from equipmentVisitor would be here

ここで、このコードが関数に含まれていると想像してください。

equipmentVisited* visited;
equipmentVisitor* visitor;
// ...
// Here you initialise visited and visitor in any convenient way
// ...
visited->accept(visitor);

ダブルディスパッチメカニズムのおかげで、この最後の行では、実際の静的タイプに関係なく、 anyがanyequipmentVisitedを受け入れることができます。equipmentVisitor最終的には、適切なクラスに対して適切な関数が呼び出されます。

要約すると:

  • 最初のディスパッチはaccept()適切なクラスを呼び出します
  • 2番目のディスパッチは、最初のディスパッチによって選択されたクラスの適切な関数を呼び出します
于 2010-07-16T08:03:58.437 に答える