Что такое одиночная и двойная отправка? - PullRequest
7 голосов
/ 16 июля 2010

Я написал шаблон для посетителей следующим образом, но я не понимаю, что такое одиночная и двойная отправка. AFAIK, одиночная отправка вызывает метод, основанный на типе вызывающего, где двойная отправка вызывает метод, основанный на типе вызывающего и типе аргумента.

Я предполагаю, что двойная диспетчеризация происходит в иерархии одного класса, но почему у класса посетителя есть иерархия двух классов, но она все еще рассматривается как двойная диспетчеризация.

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 ]

9 голосов
/ 16 июля 2010

Короче говоря, одна отправка - это когда метод полиморфен по типу одного параметра (включая неявный this).Двойная диспетчеризация - это полиморфизм двух параметров.

Типичным примером первого является стандартный виртуальный метод, который полиморфен для типа содержащего объекта.И второй может быть реализован через шаблон Visitor.

[update] Я предполагаю, что в вашем примере floppyDisk, processor и computer каждый наследуют от общегобазовый класс, который определяет accept как виртуальный метод.Точно так же методы visit* должны быть объявлены виртуальными в equipmentVisitor, которые должны иметь несколько производных классов с различными реализациями visit*. [/ update]

Исходя из вышесказанного, accept является полиморфным как для this, так и equipmentVisitor.Каждый из дисковода, процессора и компьютера имеет собственную реализацию accept, поэтому, когда посетитель вызывает accept, вызов отправляется в зависимости от типа вызываемого.Затем вызываемый абонент вызывает метод посещения, относящийся к типу посетителя, и этот вызов отправляется на основе фактического типа посетителя.

Теоретически возможна также тройная, четверная и т. Д. Отправка, хотя я никогда не виделэто реализовано на практике (в языках, которые не поддерживают двойные и более высокие диспетчеры по своей природе, то есть - мне кажется, я помню, что Smalltalk поддерживает?).Двойная диспетчеризация с использованием Visitor в C ++ и подобных языках сама по себе уже ошеломляет, поэтому реализация тройной и более высокой диспетчеризации будет слишком сложной для использования в реальных приложениях.

6 голосов
/ 16 июля 2010

В вашем примере вам не хватает основ механизма: наследования и виртуальности.Давайте предположим следующую иерархию классов, в дополнение к вашему коду:

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);

Благодаря механизму двойной отправкиэта последняя строка позволяет любому equipmentVisited принимать любые equipmentVisitor, независимо от того, каковы их действительные статические типы.В конце концов, для правильного класса будет вызвана правильная функция.

Подведем итог:

  • Первая диспетчеризация вызывает accept() для соответствующего класса
  • Втораяdispatch вызывает соответствующую функцию для класса, выбранного первой диспетчеризацией
...