Подтвердить что ты не робот

Dyamic vs Static Polymorphism в С++: что предпочтительнее?

Я понимаю, что динамический/статический полиморфизм зависит от дизайна приложения и требований. Однако целесообразно ли ВСЕГДА выбрать статический полиморфизм по сравнению с динамическим, если это возможно? В частности, я вижу в своем приложении два варианта дизайна, оба из которых, как представляется, не рекомендуются:

  • Реализовать статический полиморфизм с использованием CRTP: нет служебных данных поиска vtable, все еще предоставляя интерфейс в виде базового класса шаблона. Но, использует много переключателей и static_cast для доступа к правильному классу/методу, который опасен

  • Динамический полиморфизм: реализация интерфейсов (чистых виртуальных классов), связывание стоимости поиска для даже тривиальных функций, таких как accessors/mutators

Мое приложение очень критично, поэтому я в пользу статического полиморфизма. Но нужно знать, является ли использование слишком большого количества static_cast показателем плохого дизайна и как избежать этого, не вызывая латентности.

EDIT: Спасибо за понимание. Принимая конкретный случай, какой из них лучше подходит?

class IMessage_Type_1
{
  virtual long getQuantity() =0;
...
}
class Message_Type_1_Impl: public IMessage_Type_1
{
  long getQuantity() { return _qty;}
...
}

ИЛИ

template <class T>
class TMessage_Type_1
{
  long getQuantity() { return static_cast<T*>(this)->getQuantity(); }
...
}
class Message_Type_1_Impl: public TMessage_Type_1<Message_Type_1_Impl>
{
  long getQuantity() { return _qty; }
...
}

Обратите внимание, что в каждом классе есть несколько мутаторов/аксессуаров, и мне нужно указать интерфейс в моем приложении. В статическом полиморфизме я переключаюсь только один раз - для получения типа сообщения. Однако в динамическом полиморфизме я использую виртуальные функции для вызова метода EACH. Разве это не значит использовать статический поли? Я считаю, что static_cast в CRTP вполне безопасен и не имеет ограничения по производительности (время компиляции)?

4b9b3361

Ответ 1

Коммутатор - это не что иное, как последовательность переходов, которые после оптимизации становятся прыжком к адресу, просматриваемому таблицей. Точно как вызов виртуальной функции.

Если вам нужно прыгать в зависимости от типа, вы должны сначала выбрать тип. Если выбор не может быть выполнен во время компиляции (по существу, потому что он зависит от ввода), вы должны всегда выполнять две операции: выбирать и прыгать. Синтаксический инструмент, который вы используете для выбора, не изменяет производительность, так как оптимизируйте его.

На самом деле вы изобретаете v-таблицу.

Ответ 2

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

Одна грубая категоризация двух: виртуальные функции позволяют различать реализации для общего интерфейса; шаблоны позволяют интерфейсы для общей реализации.

Ответ 3

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

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

ИМХО это не вопрос. Придерживайтесь виртуальных функций, пока не указывается иначе. Виртуальные вызовы функций намного быстрее, чем большинство людей думают (вызов функции из динамически связанной библиотеки также добавляет слой косвенности. Никто, кажется, не думает об этом).

Я бы рассматривал только шаблонный дизайн, если он упрощает чтение кода (общие алгоритмы), вы используете один из немногих случаев, которые, как известно, медленны с виртуальными функциями (числовые алгоритмы), или вы уже определили его как узкое место в производительности.

Ответ 4

Статический полиморфизм может обеспечить значительное преимущество, если вызываемый метод может быть встроен компилятором. Например, если виртуальный метод выглядит так:

protected:
virtual bool is_my_class_fast_enough() override {return true;}

тогда статический полимофизм должен быть предпочтительным (в противном случае метод должен быть честным и возвращать false:).

"True" виртуальный вызов (в большинстве случаев) не может быть встроен.

Другие отличия (например, дополнительная косвенность в вызове vtable) не учитываются

[EDIT]

Однако, если вам действительно нужен полиморфизм времени выполнения (если вызывающий не должен знать реализацию метода и, следовательно, метод не может быть встроен на стороне вызывающего абонента), то не изобретайте vtable (как упоминал Эмилио Гаравалья), просто используйте его.