0

機能的に同一なのは、コールバックを使用してクラスの動作をカスタマイズすることと、仮想関数と継承を使用することです。しかし、私自身の作業では、関数オブジェクトを使用する方が少し柔軟であることがわかりました。

継承と多くのクラスを使用して動作をカスタマイズする

class Animal
{
    virtual void onTouch() = 0 ;
} ;

class Dog : public Animal
{
    void onTouch() 
    {
        // all dogs behave this way.
        // to change I'd need to subclass, or add parameters.
        puts( "Woof" ) ;
    }
} ;

関数オブジェクトを使用してインスタンスごとにクラスの動作をカスタマイズする

struct Animal
{
    function<void ()> onTouch ;
} ;


Animal dog1,dog2 ;
dog1.onTouch = dog2.onTouch = [](){
    puts( "Woof" ) ;
} ;

Animal dog3 ;
dog3.onTouch = [](){
    // slightly modified behavior from dog1 and dog2, without
    // having to subclass, add members, or pass extra parameters
    puts( "Arr.. Woof" ) ;
} ;

私がここでやっていることの名前、またはこれを使用しない理由を知っている人はいますか

4

2 に答える 2

3

gotoこれが仮想関数よりも「わずかに柔軟」であると言うのは、if/else よりもわずかに柔軟であると言っているようなものです。代替手段を使用すると、次のことができます。

  • その場でオブジェクトの動作を変更する (新しいオブジェクトを割り当てるだけで)
  • これらを複数持つクラスの動作を組み合わせ、逆に、そのようなクラスで 2 つの関連する実装が常に一緒に使用されることを強制しないでください。
  • 基本的に、クラスのすべてのインスタンス化をサブクラスにします。

ただし、関数オブジェクトは「その」オブジェクトの保護されたメンバーにアクセスできないため、関数オブジェクトが必要なことを実行できるように、ホスト クラスのカプセル化を解除する必要がある場合があります。

全体として、これはオブジェクトを、そのオブジェクトによって開始される外部動作(ボタン クリック ハンドラなど)に関連付けるには良い方法のように思えますが、それを使用してクラス固有の動作を拡張すると、さらに多くの問題が生じるようです。それが解決すること。

このパラダイムの最も一般的な使用例は、コールバック関数またはイベント ハンドラーです。このクラスは、ボタンの描画とマウス イベントの処理 (ボタン クラスの場合) の仕組み、またはネットワーク プロトコルの管理 (ボタン クラスの場合) に関係しているためです。ネットワークソケットクラス)。このような場合、イベント ハンドラーを仮想化してサブクラスに実装することは、オブジェクト指向の設計として適切ではありません。ボタンをサブクラス化する必要がないように、サブクラスは概念的に親クラスの新しい「型」ではないからです。高さと幅を変更します。

于 2012-11-18T04:14:24.427 に答える