9

オブジェクト指向では、通常、インターフェイスを使用してコールバックを実装します: (大まかな例)

class Message {}

class IMsgProcessor {
public:
     virtual void handle_msg(const Message& msg) = 0;
}

class RequestMsgProcessor : public IMsgProcessor {
     virtual void handle_msg(const Message& msg)  {
     // process request message
    }
}

class CustomSocket {
public:
   Socket(IMsgProcessor* p) : processor_(p) {}

   void receive_message_from_network(const Message& msg) {
       // processor_ does implement handle_msg. Otherwise a compile time error. 
       // So we've got a safe design.
       processor_->handle_msg(msg);
   }
private:
   IMsgProcessor* processor_;
}

ここまでは順調ですね。C++11 では、これを行う別の方法として、CustomSocket に std::function オブジェクトのインスタンスを受け取るだけにする方法があります。実装されている場所や、オブジェクトが null 以外の値であるかどうかは気にしません。

class CustomSocket {
public:
   Socket(std::function<void(const Message&)>&& f) : func_(std:forward(f)) {}

   void receive_message_from_network(const Message& msg) {
       // unfortunately we have to do this check for every msg.
       // or maybe not ...
       if(func_)
            func_(msg);
   }
private:
   std::function<void(const Message&)> func_;
}


1. パフォーマンスへの影響はどうですか? 仮想関数呼び出しは関数オブジェクトを呼び出すよりも速いと思いますが、どれくらい速いですか? 私は高速メッセージング システムを実装していますが、不必要なパフォーマンスの低下は避けたいと考えています。
2. ソフトウェア エンジニアリングの実践に関しては、私は 2 番目のアプローチの方が好きだと言わざるを得ません。より少ないコード、より少ないファイル、より少ない混乱: インターフェイス クラスはありません。柔軟性の向上: 一部の関数オブジェクトを設定し、他のオブジェクトを null のままにすることで、インターフェイスのサブセットのみを実装できます。または、(単一のサブクラスではなく) 個別のクラス、フリー関数、または両方の組み合わせによって、インターフェイスのさまざまな部分を実装することができます。さらに、CustomSocket は、IMsgProcessor のサブクラスだけでなく、任意のクラスで使用できます。これは私の意見では、大きな利点です。
あなたは何を言っていますか?これらの議論に何か根本的な欠陥があると思いますか?

4

3 に答える 3

3

両方の長所を活かすことができます

template<class F>
class MsgProcessorT:public IMsgProcessor{
  F f_;
  public:
  MsgProcessorT(F f):f_(f){}
  virtual void handle_msg(const Message& msg)  {
      f_(msg);
 }

};
template<class F>
IMsgProcessor* CreateMessageProcessor(F f){
    return new MsgProcessor<T>(f);

};

次に、このように使用できます

Socket s(CreateMessageProcessor([](const Message& msg){...}));

または、さらに簡単にするために、別のコンストラクターを Socket に追加します

class Socket{
...
template<class F>
Socket(F f):processor_(CreateMessageProcessor(f){}


};

それからあなたはすることができます

Socket s([](const Message& msg){...});

仮想関数呼び出しと同じ効率性を維持

于 2012-11-29T15:06:18.677 に答える
2

インターフェイスのアプローチはより伝統的ですが、より冗長でもあります。何が行われるかは明確でMessageProcessorあり、チェックする必要はありません。さらに、1つの同じオブジェクトを多くのソケットで再利用できます。

std::functionアプローチはより一般的です:を受け入れるものなら何でも使用operator()(Message const&)できます。ただし、冗長性がないため、コードが読みにくくなる危険性があります。

パフォーマンスのペナルティについてはわかりませんが、大きな違いがあると驚くでしょう。

これがあなたのコードの本質的な部分であるなら、私はインターフェースアプローチに固執するでしょう(そう思われるように)。

于 2012-11-29T14:56:14.733 に答える
1

どちらの例でも、実際にはインターフェイスを使用しています。異なるのは、それらを定義する方法です。最初のケースでは、インターフェースは純粋仮想関数を備えた従来のクラスであり、2番目のケースでは、インターフェースは関数参照です。これは、設計の観点からC関数ポインターとは大きく異なることに注意してください。私の意見では、特定の要件に応じて両方のバリエーションを組み合わせて、新しいケースごとに長所と短所(あなたが述べたようなもの)を検討することができます。パフォーマンスへの影響に関しては、テストを実行し、結果を比較して、パフォーマンス要件に一致させることが最善の答えだと思います。

于 2012-11-29T14:57:27.127 に答える