Мой метод для предотвращения dynamic_cast <> быстрее, чем dynamic_cast <> сам?

Я отвечал на вопрос несколько минут назад, и он повысил до меня другой:

В одном из моих проектов я делаю некоторый парсинг сетевого сообщения. Сообщения в форме:

[1 byte message type][2 bytes payload length][x bytes payload]

Формат и содержание полезной нагрузки определяются типом сообщения. У меня есть иерархия классов, на основе общего класса Message.

Для инстанцирования моих сообщений у меня есть статический метод парсинга, который отдает a Message* в зависимости от байта типа сообщения. Что-то как:

Message* parse(const char* frame)
{
  // This is sample code, in real life I obviously check that the buffer
  // is not NULL, and the size, and so on.

  switch(frame[0])
  {
    case 0x01:
      return new FooMessage();
    case 0x02:
      return new BarMessage();
  }

  // Throw an exception here because the mesage type is unknown.
}

Я иногда должен получать доступ к методам подклассов. Так как моя обработка сетевого сообщения должна быть быстрой, я обманул для предотвращения dynamic_cast<> и я добавил метод к основе Message класс, который отдает тип сообщения. В зависимости от этого возвращаемого значения я использую a static_cast<> правильному ребенку вводят вместо этого.

Я сделал это главным образом, потому что мне сказали однажды это dynamic_cast<> было медленным. Однако я не знаю точно, что это действительно делает и насколько медленный это, таким образом, мой метод мог бы быть как так же, как медленный (или медленнее), но намного более сложен.

Что делает Вас, парни думают об этом дизайне? Действительно ли это распространено? Это действительно быстрее, чем использование dynamic_cast<> ? Любое подробное объяснение того, что происходит под капотом когда одно использование dynamic_cast<> приветствуется!

---РЕДАКТИРОВАНИЕ---

Так как некоторые люди спросили почему:

В основном, когда я принимаю кадр, я делаю две вещи:

  1. Я анализирую сообщение и создаю соответствующий экземпляр подкласса Message если содержание кадра допустимо. Нет никакой логики за исключением части парсинга.
  2. Я получаю a Message и в зависимости от a switch(message->getType()), Я static_cast<> к правильному типу и делают то, что должно быть сделано с сообщением.

7
задан Community 23 May 2017 в 12:04
поделиться

8 ответов

Реализации dynamic_cast, конечно, зависят от компилятора.

В Visual C ++ vtable указывает на структуру, которая содержит все RTTI о структуре. Поэтому dynamic_cast включает разыменование этого указателя и проверку «фактического» типа на соответствие запрошенному типу и выдачу исключения (или возвращение NULL), если они несовместимы. Это в основном эквивалент системы, которую вы описываете. Это не медленная особенность.

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

7
ответ дан 6 December 2019 в 09:59
поделиться

Единственный правильный ответ на «это быстрее?» - «попробуйте».

4
ответ дан 6 December 2019 в 09:59
поделиться

Когда люди говорят, что dynamic_cast медленный, это просто эмпирическое правило. dynamic_cast более или менее делает то, что вы делаете. Он медленный, потому что включает пару обращений к памяти. Это похоже на то, как люди говорят, что виртуальные функции медленные. Вы берете что-то быстрое (вызов функции) и добавляете к этому пару обращений к памяти. Это значительно замедляет работу (поскольку весь путь к памяти и обратно может занять несколько сотен циклов), но для большинства людей это не имеет значения, пока это не делается часто (с очень большим значением "часто").

3
ответ дан 6 December 2019 в 09:59
поделиться

Вы сосредотачиваетесь на скорости, но как насчет правильности?

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

template <class T>
T* convert(Message* message)
{
  if (message == 0) return 0;
  else return message->getType() == T::Type() ? static_cast<T*>(message) : 0;
}

Для того, чтобы встроить тест и преобразовать его в единственную функцию, чтобы избежать таких ошибок, как:

switch(message->getType())
{
case Foo:
{
  //...
  // fallthrough
}
case Bar:
{
  BarMessage* bar = static_cast<BarMessage*>(message); // got here through `Foo`
}
}

или очевидного:

if (message->getType() == Foo) static_cast<BarMessage*>(message); // oups

Not по общему признанию, много, но это тоже не так много усилий.

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

  • виртуальные методы
  • Посетитель

и т. Д.

1
ответ дан 6 December 2019 в 09:59
поделиться

Я не вижу никаких ответов по этому поводу, но вы не можете отправлять объекты C ++ по сети и ожидать, что они прибудут в целости и сохранности. Виртуальная таблица настраивается на основе состояния памяти на передающем компьютере, вполне вероятно, что на принимающем компьютере не будет вещей в одном и том же месте. Это также обычно приводит к сбою RTTI (что и использует dynamic_cast), поскольку RTTI часто реализуется вместе с vtable.

-1
ответ дан 6 December 2019 в 09:59
поделиться

У вас уже есть абстрактный базовый класс " Сообщение". Используйте его как интерфейс, чтобы скрыть детали реализации FooMessage и BarMessage.

Думаю, именно поэтому вы выбрали этот подход, или нет?

0
ответ дан 6 December 2019 в 09:59
поделиться

A) Это очень похоже на преждевременную оптимизацию.

Б) Если ваш проект требует так много вызовов dynamic_cast<>, что вы беспокоитесь об этом, то вам определенно нужно взглянуть на свой проект и понять, что в нем не так.

C) Как сказано в предыдущем ответе, единственный способ ответить, быстрее ли это, это использовать профилировщик (или эквивалент) и провести сравнение.

2
ответ дан 6 December 2019 в 09:59
поделиться

Это зависит от того, как вы управляете своими сообщениями. Когда у меня есть switch для выбора сообщения на основе типа, лучшим вариантом будет использование static_cast, так как вы знаете, что парсер функции даст вам создать правильный тип.

Message* gmsg parse(frame);

switch (gmsg->type) {
  case FooMessage_type:
    FooMessage* msg=static_cast<FooMessage*)(gmsg);
    // ...
    break;
  case BarMessage_type:
    BarMessage* msg=static_cast<BarMessage*)(gmsg);
    //...
    break;      
};

Использование dynamic_cast здесь является чрезмерной защитой.

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

switch (frame::get_msg_type(aframe)) {
  case FooMessage_type:
    FooMessage msg=parse<FooMessage)(aframe);
    // work with msg
    break;
  case BarMessage_type:
    BarMessage msg=parse<BarMessage)(aframe);
    //...
    break;
};

где parse разбирает фрейм как MSG, или бросает исключение, когда разбор не удался.

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

3
ответ дан 6 December 2019 в 09:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: