7

数分前に質問に答えていたところ、別の質問が浮かびました。

私のプロジェクトの 1 つで、ネットワーク メッセージの解析を行っています。メッセージの形式は次のとおりです。

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

ペイロードの形式と内容は、メッセージ タイプによって決まります。共通の class に基づくクラス階層がありますMessage

Message*メッセージをインスタンス化するために、メッセージの種類に応じてbyteを返す静的解析メソッドがあります。何かのようなもの:

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この戻り値に応じて、static_cast<>代わりに右の子型に a を使用します。

私がこれを行った主な理由は、これdynamic_cast<>は遅いと言われたことがあるからです。ただし、それが実際に何をするのか、どのくらい遅いのかは正確にはわかりません。したがって、私の方法は同じくらい遅い (または遅い) かもしれませんが、はるかに複雑です。

このデザインどう思いますか?それは一般的ですか?を使用するよりも本当に速いdynamic_cast<>ですか?1回の使用で内部で何が起こるかについての詳細な説明dynamic_cast<>は大歓迎です!

- - 編集 - -

一部の人々が理由を尋ねたので:

基本的に、フレームを受信すると、次の 2 つのことを行います。

  1. Messageメッセージを解析し、フレームのコンテンツが有効である場合、対応するサブクラスのインスタンスを構築します。解析部分以外にロジックはありません。
  2. 私は を受け取り、にMessage応じて適切なタイプに移動し、メッセージに対して必要な処理を行います。switch(message->getType())static_cast<>
4

9 に答える 9

7

もちろん、dynamic_cast の実装はコンパイラによって異なります。

Visual C++ では、vtable は構造体に関するすべての RTTI を含む構造体を指します。したがって、dynamic_cast には、このポインターの逆参照、要求された型に対する「実際の」型のチェック、互換性がない場合は例外のスロー (または NULL の戻り) が含まれます。それは基本的にあなたが説明するシステムと同等です。特に遅いわけではありません。

あなたの設計も少しずれているように聞こえます。オブジェクトの真の型を忘れるファクトリ メソッドがあり、すぐにその情報を忘れないようにする必要があります。おそらく、型を忘れないようにするときに行うそのロジックを、ファクトリ メソッドに移動するか、基本クラス自体の仮想メソッドに移動する必要があります。

于 2010-05-03T14:43:27.947 に答える
4

「速いか」に対する唯一の正解は「試してみること」です。

于 2010-05-03T13:44:29.520 に答える
3

これは、メッセージの管理方法によって異なります。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として解析するか、解析が失敗したときに例外をスローします。

仮想関数を使用するように指示している他の回答があります。このメッセージのオブジェクト指向デザインには、実際には何の利点もありません。

于 2010-05-03T14:22:37.303 に答える
3

dynamic_cast が遅いと人々が言うとき、それは単なる経験則です。dynamic_cast は多かれ少なかれあなたがしていることをします。数回のメモリアクセスを伴うため、低速です。仮想関数が遅いと人々が言うときのようなものです。あなたは何か速いもの(関数呼び出し)を取り、それにいくつかのメモリアクセスを追加しています。これはかなりの速度低下です (ラムとバックに数百サイクルかかる可能性があるため) が、頻繁に実行されない限り (頻繁に非常に大きな値を使用)、ほとんどの人にとっては問題になりません。 .

于 2010-05-03T14:46:12.930 に答える
2

A) これは時期尚早の最適化のように聞こえます。

B) あなたのデザインがあまりにも多くの dynamic_cast<> の呼び出しを必要とする場合、あなたは間違いなくあなたのデザインを見て何が悪いのかを理解する必要があります.

C)前の回答が言ったように、それが速いかどうかを答える唯一の方法は、プロファイラー(または同等のもの)を使用して比較することです。

于 2010-05-03T13:47:36.367 に答える
1

スピード重視ですが、正確さは?

根底にある質問は、間違いを犯さないと確信していますか? 特に、次のような方法でキャスト メソッドをラップしたくなるかもしれません。

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

確かにそれほど多くはありませんが、それほど労力もかかりません。

一方で、ランタイム ディスパッチを適用してテクニックを確認することもできます。

  • virtualメソッド
  • Visitor

等...

于 2010-05-03T14:48:38.743 に答える
0

すでに抽象基本クラス「Message」があります。FooMessageとBarMessageの実装の詳細を隠すためのインターフェースとして使用してください。

そういうわけで、あなたはそのアプローチを選んだのですか、そうではありませんか?

于 2010-05-03T14:23:12.403 に答える
-1

これに関する回答は見当たりませんが、C++ オブジェクトをネットワーク経由で送信して、そのままの状態で到着することを期待することはできません。仮想テーブルは、送信側コンピューターのメモリの状態に基づいて設定されます。受信側コンピューターが同じ場所にあるとは限りません。RTTI は vtable と共に実装されることが多いため、これは通常、RTTI を失敗させます (これは dynamic_cast が使用するものです)。

于 2010-05-03T21:43:39.890 に答える