4

投稿で読んだ内容と少し混乱しています:case-vs-if-else-if-which-is-more-effective

長い case/if-else ステートメントをポリモーフィズムの使用に置き換える必要があることが何度も提案されています。私はそれが実際に何を意味するのか頭を悩ませようとしています. どのように置き換えることができますか:

case TASK_A:
    // do things for task A
    break;
case TASK_B:
    // do things for task B
    break;
        :
        :
case TASK_J:
    // do things for task J
    break;

ポリモーフィズムで?「do ...」の部分が基本的に同じ繰り返しである場合は、ある程度理解できますが、「ケース」の一部またはすべてに大きな違いがある場合、これはまだ適用されますか?

4

6 に答える 6

5

リンク先の例では、オブジェクトのタイプswitchを超えています。提案は、ポリモーフィズムを使用してタイプをチェックする必要をなくすことです。つまり、基本クラスで仮想関数を宣言し、具象クラスごとにそれをオーバーライドして必要なことを行い、全体をその関数の呼び出しに置き換えます。switch

あなたの場合、オブジェクトの型ではなく、変数の値をテストしています。ただし、必要に応じてポリモーフィック ソリューションに変換することもできます。

struct Task         {virtual void do_things() = 0;};
struct TaskA : Task {virtual void do_things() {/*do things for task A*/}};
struct TaskB : Task {virtual void do_things() {/*do things for task B*/}};
//...
struct TaskJ : Task {virtual void do_things() {/*do things for task J*/}};

次に、切り替えている変数を への(スマート)ポインターに置き換えることができますTask。とスイッチtask->do_things()。それが aよりも優れているかどうかswitchは好みの問題です。

于 2013-10-03T11:48:25.287 に答える
4

taskたとえば、子クラスがオーバーライドする関数 (潜在的に抽象) を定義する親クラス/インターフェイスを作成します。この関数を呼び出しましょうhandle_task

次に、各タイプのタスク (つまり、case上記のステートメント)ごとに子クラスを作成し// do things for task X、そのクラスの実装に を配置します。handle_task

ポリモーフィズムにより、これらの子クラスはそれぞれ、親クラスのインスタンスとして受け渡されたり、親クラスのインスタンスとして扱われたりすることができhandle_task、それらを呼び出すと正しいコードが実行されます。

簡単な例:

#include <iostream>

class Task {
  public:
    virtual void handle_task()
    {
      std::cout << "Parent task" << std::endl;
    }
};

class Task_A: public Task {
  public:
    void handle_task()
    {
      std::cout << "task a" << std::endl;
    }
};

class Task_B: public Task {
  public:
    void handle_task()
    {
      std::cout << "task b" << std::endl;
    }
};

int main( void )
{
  Task *task;
  Task_A a;
  Task_B b;

  task=&a;
  task->handle_task();
  task=&b;
  task->handle_task();

}

印刷します

/tmp$ g++ test.cpp
/tmp$ ./a.out
task a
task b
于 2013-10-03T11:43:32.250 に答える
3

主な設計上の理由は、ポリモーフィズムにより、共通コードに触れることなくコードを切り離して拡張できるようにすることです。追加の効率の理由は、考えられるコード パスを線形検索する必要がなく、代わりに目的のアクションに無条件にジャンプできることです (ただし、これは実装の詳細です)。

以下は、光り輝くかもしれないポリモーフィズムの純粋な C バージョンです。

// Switch-based:

void do_something(int action, void * data)
{
    switch(action)
    {
        case 1: foo(data); break;
        case 2: bar(data); break;
        case 3: zip(data); break;
        default: break;
    }
}

// Polymorphic:

typedef void (*action_func)(void *);

void do_something(action_func f, void * data)
{
    f(data);
}

ご覧のとおり、2 番目のバージョンの方が読みやすく、維持しやすく、新しいアクションを追加する場合に変更する必要はありません。

于 2013-10-03T11:49:15.750 に答える
2

重要なポイントの 1 つは、デカップリングです。上記のコードでは、どのケースが存在するかを知る必要があります。毎回それらすべてをリストする必要があります。switch 分岐からのロジックを仮想メソッドに配置すると、呼び出しコードは不要になります。

  • どのようなケースが実際に存在し、
  • 各ケースのロジックは何ですか。

代わりに、ロジックはクラス内の属する場所に配置されます。

ここで、別のケースを追加することを検討してください。コードでは、そのような switch ステートメントが使用されているプログラム内のすべての場所に触れる必要があります。それらを見つけなければならないだけでなく (どれも見逃さないでください!)、他の人が使用するある種のライブラリのコードを書いているので、それらは自分のコードにないかもしれません。仮想メソッドを使用すると、新しいクラスで必要に応じていくつかのメソッドをオーバーライドするだけで、すべてがすぐに機能します。

  BaseTask = class
  {
    virtual void Perform() = 0;
  }

  TaskOne = class(BaseTask)
  {
    void Perform() { cout << "Formatting hard disk ..."; }
  }

  TaskTwo = class(BaseTask)
  {
    void Perform() { cout << "Replacing files with random content ..."; }
  }

だから今、呼び出しコードはする必要があります

foreach( BaseTask task in Tasks)  // pseudo code    
{
  task.Perform();
}

ここで、別のタスクを追加するとします。

  TaskThree = class(BaseTask)
  {
    void Perform() { cout << "Restoring everything form the backup..."; }
  }

これで完了です。スイッチの編集もケースの追加もありません。それはどれほどクールですか?

于 2013-10-03T11:45:23.957 に答える
0

クラスAnimalを取る:2つのサブクラスとして:DogおよびBird

feed()Dog で呼び出すか Bird で呼び出すかによって異なる関数を実装します。

これを作る代わりに:

if object is dog
    object.dog.feed()
else
    object.bird.feed()

あなたはただ行う:

object.feed()
于 2013-10-03T11:46:19.067 に答える
0

Base基本的に、純粋な仮想メンバー関数を持つ基本クラスがありますdo_task()DerivedA次に、DerivedA.. DerivedJfromを継承し、Baseそれらはすべて .. の独自のバージョンを定義しますdo_task。次に、次のように呼び出します。

std::shared_ptr<Base> obj = // points to a Derivedx

// ....

obj->do_task()
于 2013-10-03T11:47:04.360 に答える