Как избежать больших if-операторов и instanceof

Animal

public abstract class Animal {
 String name;

 public Animal(String name) {
  this.name = name;
 }

}

Lion

public class Lion extends Animal {

 public Lion(String name) {
  super(name);
  // TODO Auto-generated constructor stub
 }

 public void roar() {
  System.out.println("Roar");
 }
}

Deer

public class Deer extends Animal {

 public Deer(String name) {
  super(name);
 }

 public void runAway() {
  System.out.println("Running...");
 }

}

TestAnimals

public class TestAnimals {
 public static void main(String[] args) {
  Animal lion = new Lion("Geo");
  Animal deer1 = new Deer("D1");
  Animal deer2 = new Deer("D2");

  List li = new ArrayList();
  li.add(lion);
  li.add(deer1);
  li.add(deer2);
  for (Animal a : li) {
   if (a instanceof Lion) {
    Lion l = (Lion) a;
    l.roar();
   }
   if (a instanceof Deer) {
    Deer l = (Deer) a;
    l.runAway();
   }

  }
 }
}

Есть ли лучший способ перебирать список без использования приведений? В приведенном выше случае это нормально, но если у вас есть много расширений базового класса, тогда нам понадобится и столько блоков if. { public: tester (int * arr_, int sz_): arr (arr_), sz (sz_) {} ...

Я столкнулся с противоречивым поведением оптимизации с различными компиляторами для следующего кода:

class tester
{
public:
    tester(int* arr_, int sz_)
        : arr(arr_), sz(sz_)
    {}

    int doadd()
    {
        sm = 0;
        for (int n = 0; n < 1000; ++n) 
        {
            for (int i = 0; i < sz; ++i)
            {
                sm += arr[i];
            }
        }
        return sm;
    }
protected:
    int* arr;
    int sz;
    int sm;
};

Функция doadd имитирует некоторый интенсивный доступ к членам (в этом вопросе дополнительно игнорируйте переполнения). По сравнению с аналогичным кодом, реализованным как функция:

int arradd(int* arr, int sz)
{
    int sm = 0;
    for (int n = 0; n < 1000; ++n) 
    {
        for (int i = 0; i < sz; ++i)
        {
            sm += arr[i];
        }
    }
    return sm;
}

Метод doadd работает примерно в 1,5 раза медленнее, чем функция arradd при компиляции в режиме выпуска с Visual C ++ 2008. Когда Я изменяю метод doadd , чтобы он выглядел следующим образом (псевдоним всех членов с помощью locals):

int doadd()
{
    int mysm = 0;
    int* myarr = arr;
    int mysz = sz;
    for (int n = 0; n < 1000; ++n) 
    {
        for (int i = 0; i < mysz; ++i)
        {
            mysm += myarr[i];
        }
    }
    sm = mysm;
    return sm;
}

Время выполнения становится примерно таким же. Правильно ли я заключил, что компилятор Visual C ++ упустил эту оптимизацию? g ++ , кажется, делает это лучше и запускает и функцию-член, и обычную функцию с одинаковой скоростью при компиляции с -O2 или -O3 .


Тестирование выполняется путем вызова члена doadd и функции arradd для некоторого достаточно большого массива (размером в несколько миллионов целых чисел).


EDIT: Some fine- Детальное тестирование показывает, что главным виновником является член sm . Замена всех остальных локальными версиями по-прежнему увеличивает время выполнения, но как только я заменю sm на mysm , время выполнения станет равным версии функции.


alt text

Решение

Разочарован ответы (извините, ребята), Я стряхнул с себя лень и погрузился в листинги разборки этого кода. Мой ответ ниже обобщает результаты. Вкратце: это не имеет ничего общего с наложением имен, это связано с развертыванием цикла и с некоторыми странными эвристиками, которые MSVC применяет при принятии решения, какой цикл развернуть.

15
задан Community 23 May 2017 в 10:30
поделиться