Dyamic против статического полиморфизма в C ++: что предпочтительнее? - PullRequest
18 голосов
/ 28 марта 2012

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

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

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

РЕДАКТИРОВАТЬ: Спасибо за понимание.Возьмем конкретный случай, какой из них лучше?

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; }
...
}

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

Ответы [ 4 ]

14 голосов
/ 28 марта 2012

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

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

8 голосов
/ 28 марта 2012

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

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

Фактически вы заново изобретаете виртуальную таблицу.

6 голосов
/ 28 марта 2012

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

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

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

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

1 голос
/ 28 марта 2012

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

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

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

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

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

[EDIT]

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...