Многократная отправка в C ++ - PullRequest
35 голосов
/ 17 ноября 2009

Я пытаюсь понять, что такое множественная отправка. Я прочитал много разных текстов, но до сих пор не знаю, что такое многократная рассылка и для чего она нужна. Может быть, я упускаю фрагмент кода, использующий множественную диспетчеризацию. Пожалуйста, не могли бы вы написать небольшой фрагмент кода на C ++ с использованием множественной диспетчеризации, чтобы я мог видеть, что он не может быть правильно скомпилирован / запущен, потому что C ++ имеет только одну диспетчеризацию? Мне нужно увидеть разницу. Спасибо.

Ответы [ 4 ]

64 голосов
/ 17 ноября 2009

Multi-dispatch - это возможность выбрать, какую версию функции вызывать, основываясь на типе времени выполнения аргументов, переданных в вызов функции.

Вот пример, который не будет работать правильно в C ++ (не проверено):

class A { };
class B : public A { };
class C : public A { }


class Foo
{
  virtual void MyFn(A* arg1, A* arg2) { printf("A,A\n"); }
  virtual void MyFn(B* arg1, B* arg2) { printf("B,B\n"); }
  virtual void MyFn(C* arg1, B* arg2) { printf("C,B\n"); }
  virtual void MyFn(B* arg1, C* arg2) { printf("B,C\n"); }
  virtual void MyFn(C* arg1, C* arg2) { printf("C,C\n"); }
};

void CallMyFn(A* arg1, A* arg2)
{
  // ideally, with multi-dispatch, at this point the correct MyFn() 
  // would be called, based on the RUNTIME type of arg1 and arg2
  pFoo->MyFn(arg1, arg2);
}

...

A* arg1 = new B();
A* arg2 = new C();
// Using multi-dispatch this would print "B,C"... but because C++ only
// uses single-dispatch it will print out "A,A"
CallMyFn(arg1, arg2);
20 голосов
/ 17 ноября 2009

Многократная отправка - это когда выполняемая функция зависит от типа времени выполнения более одного объекта.

C ++ имеет одну отправку, потому что при использовании виртуальных функций фактическая функция, которая запускается, зависит только от типа времени выполнения объекта слева от -> или. оператор.

Я изо всех сил пытаюсь придумать реальный программный пример для множественной отправки. Может быть, в игре, где разные персонажи сражаются друг с другом.

void Fight(Opponent& opponent1, Opponent& opponent2);

Победитель боя может зависеть от характеристик обоих противников, поэтому вы можете захотеть, чтобы этот вызов отправлял одно из следующих, в зависимости от типов обоих аргументов во время выполнения:

void Fight(Elephant& elephant, Mouse& mouse)
{
    mouse.Scare(elephant);
}

void Fight(Ninja& ninja, Mouse& mouse)
{
    ninja.KarateChop(mouse);
}

void Fight(Cat& cat, Mouse& mouse)
{
    cat.Catch(mouse);
}

void Fight(Ninja& ninja, Elephant& elephant)
{
    elephant.Trample(ninja);
}

// Etc.

То, что делает функция, зависит от типов обоих аргументов, а не только от одного. В C ++ вам, возможно, придется написать это как некоторые виртуальные функции. Виртуальная функция будет выбрана в зависимости от одного аргумента (указатель this). Тогда виртуальная функция может нуждаться в переключателе или чем-то еще, чтобы сделать что-то конкретное с другим аргументом.

11 голосов
/ 17 ноября 2009

См. Эту статью, написанную Б. Страуструпом: Открытые мульти-методы для C ++

3 голосов
/ 17 ноября 2009

В единичная отправка выполняемая функция зависит только от типа объекта. В двойная отправка выполняемая функция зависит от типа объекта и параметр.

В следующем примере функция Area() вызывается с помощью однократная отправка, и Intersect() полагается на двойную отправку, потому что она занимает Параметр формы.

class Circle;
class Rectangle;
class Shape
{
    virtual double Area() = 0; // Single dispatch

    // ...
    virtual double Intersect(const Shape& s) = 0; // double dispatch, take a Shape argument
    virtual double Intersect(const Circle& s) = 0; 
    virtual double Intersect(const Rectangle& s) = 0; 
};

struct Circle : public Shape
{
    virtual double Area() { return /* pi*r*r */; }

    virtual double Intersect(const Shape& s); 
    { return s.Intersect(*this)  ; }
    virtual double Intersect(const Circle& s); 
    { /*circle-circle*/ }
    virtual double Intersect(const Rectangle& s); 
    { /*circle-rectangle*/ }
};

Пример основан на этой статье .

...