Динамическое приведение или перегрузка функций? - PullRequest
3 голосов
/ 17 сентября 2011

Рассмотрим следующий абстрактный класс:

class Abstract {
public:
    // ...

    virtual bool operator==(const Abstract& rhs) const = 0;

    // ...
};

Теперь предположим, что я создаю несколько производных классов из этого абстрактного класса.Однако каждый из них использует свой алгоритм при сравнении со своим собственным типом и универсальный алгоритм при сравнении с любым из других производных классов.Среди следующих двух вариантов, который был бы лучшим, более эффективным вариантом

1005 * Вариант A:
class Derived : public Abstract {
public:
    // ...

    bool operator==(const Abstract& rhs) const {
        // Code for comparing to any of the other derived classes
    }

    bool operator==(const Derived& rhs) const {
        // Code for comparing to myself
    }

    // ...
};

Вариант B:

class Derived : public Abstract {
public:
    // ...

    bool operator==(const Abstract& rhs) const {
        const Derived* tmp = dynamic_cast<const Derived*>(&rhs);
        if (tmp) {
            // Code for comparing to myself
        }
        else {
            // Code for comparing to any of the other derived class
        }
    }
};

Я действительноЛюбопытно, какие преимущества и недостатки были бы у этих опций, поскольку типизация C ++ является для меня относительно загадочной темой.Кроме того, какое решение является более «стандартным», и оказывает ли второе решение какое-либо влияние на производительность?

Возможно, существует третье решение?Особенно, если было много производных классов, каждый из которых нуждался в собственном специальном алгоритме сравнения с различными производными классами?

Ответы [ 4 ]

2 голосов
/ 17 сентября 2011

Ваши два метода для разных ситуаций. Для опции A статический тип rhs используется, чтобы решить, какую функцию вызывать, а для опции B используется динамический тип.

Так что, если вы хотите, чтобы ваша программа выбирала поведение на основе «реального» типа аргумента, я думаю, вам следует выбрать второй вариант. Если типы могут быть известны во время компиляции, следует использовать параметр А., поскольку он обеспечивает лучшую производительность.

1 голос
/ 17 сентября 2011

На самом деле вы можете сделать это третьим способом, используя один из технических приемов для реализации двойной диспетчеризации .Этот подход полностью описан в пункте 31 «Более эффективный C ++».Вот небольшой пример:

#include <iostream>

class Derived1;
class Derived2;

class Base
{
public:
    virtual bool operator==( Base& other) = 0;
    virtual bool compare( Base& other) {return false;}
    virtual bool compare( Derived1& other) {return false;}
    virtual bool compare( Derived2& other) {return false;}
};

class Derived1 : public Base
{
public:
    virtual bool operator==( Base& other) {return other.compare(*this);}

    virtual bool compare( Base& other) {return false;}
    virtual bool compare( Derived1& other) {return true;}
};

class Derived2 : public Base
{
public:
    virtual bool operator==( Base& other) {return other.compare(*this);}

    virtual bool compare( Base& other) {return false;}
    virtual bool compare( Derived2& other) {return true;}
};

int main()
{
    Base *a = new Derived1;
    Base *b = new Derived1;
    Base *c = new Derived2;

    std::cout << (*a == *b) << std::endl;
    std::cout << (*a == *c) << std::endl;
    return 0;
}

Вывод:

1
0
1 голос
/ 17 сентября 2011

Я думаю, что вариант B - это то, что вы ищете, если вы ожидаете, что оператор == будет использовать динамический тип аргумента. Например:

class base
{
public:
  virtual bool operator ==( const base& other ) = 0;
};

class derived : public base
{
public:
  bool operator ==( const base& other ) { return false; }
  bool operator ==( const derived& other ) { return true; }
};


int main()
{
  base* a = new derived;
  base* b = new derived;
  std::cout << ( *a == *b ) << std::endl;
}

Это печатает:

0

Таким образом, вызывается оператор == (const base и другие), даже если фактический динамический тип производный .

0 голосов
/ 18 сентября 2011

К сожалению, в C ++ нет мультиметодов, которые бы выбирали текущую функцию для вызова на основе информации о динамическом типе.Для реализации поведения вам потребуется двойная рассылка, шаблон посетителя или какой-то другой трюк.

...