Понижение указателя с помощью функции вместо гигантского оператора if - PullRequest
2 голосов
/ 17 октября 2010

У меня есть вектор с указателями типа Vehicle.Vehicle является базовым классом, и есть много производных типов, таких как MotorCycle, Car, Plane и т. Д. Теперь в моей программе наступает момент, когда мне нужен производный тип при обходе вектора.Каждый класс Vehicle имеет функцию GetType (), которая возвращает int, который сообщает мне, что является производным типом (motorcylce, car, plan).Итак, я могу использовать динамическое приведение к понижению до производного типа от указателя базового класса.Однако мне нужно иметь гигантский оператор if каждый раз, когда мне нужен производный указатель

if(vehicle_ptr->GetType() == PLANE)
    Plane *ptr = dynamic_cast<Plane*> vehicle_ptr;
else if (vehicle_ptr->GetType() == MOTORCYCLE)
    MotorCycle *ptr = dynamic_cast<MotorCycle*> vehicle_ptr;
..and on and on.

Есть ли способ иметь функцию или какой-нибудь трюк, который я могу вызвать, который спас бы меня от гигантского оператора if везде?Like :: GetDerivedPtr (Vehicle * ptr).Поможет ли здесь шаблонный класс?(никогда не использовал их раньше) Извините, мой C ++ немного заржавел, и я искал, но эти термины приводят к слишком большому количеству материала, чтобы найти то, что я ищу.Спасибо.

Ответы [ 5 ]

4 голосов
/ 17 октября 2010

Похоже, вы пытались воссоздать полиморфизм вручную. Вам не нужен член типа. Это почти всегда плохая идея. Используйте полиморфизм и виртуальные функции.

Когда у вас есть указатель транспортного средства v и вы делаете

v->function();

Он вызовет подходящую функцию для любого типа (самолет, поезд или автомобиль), на который фактически указывает указатель, если функция является виртуальной функцией. То, что вы делаете, уже обработано языком.

Итак:

class A {
public:
    virtual void f() {cout << "A";}
};

class B : public A {
public:
    virtual void f() {cout << "B";}
};

int main(){
  A *a;
  B b;
  a = &b;
  a->f();
}

В приведенном выше фрагменте будет напечатано B.

1 голос
/ 17 октября 2010

Я придерживаюсь идеи, что вам нужны виртуальные функции и общий базовый тип.Представьте, что есть какой-то способ получить указатель правильного типа.Что ты будешь делать с этим тогда?В любом случае вам придется выполнить гигантское переключение, потому что вы вызываете определенные функции для каждого из ваших конкретных типов.

Одним из решений было бы придумать имя для операции, которую вы пытаетесь выполнить, и поместить ее реализациюкак виртуальная функция в каждом конкретном классе транспортных средств.Если операция принимает разные параметры для каждого из случаев, параметры должны быть упакованы в специальную полиморфную структуру / класс, но здесь, возможно, шаблон Visitor является более общим решением.

1 голос
/ 17 октября 2010

Сначала проверьте, можно ли выполнить то, что вы собираетесь сделать, просто с помощью виртуальных функций в классе Vehicle, переопределенных каждым производным классом.

Если нет, то рассмотрите шаблон посетителя.

Приветствия и hth.,

0 голосов
/ 17 октября 2010

Использование Посетитель на основе диспетчеризации. Заметьте, что в следующем (несколько тривиализированном) примере не требуется простого приведения любого типа:

// simple cyclic visitor
class VehicleVistor {
 public:
  // add overload for each concrete Vehicle type
  virtual void Visit(class Motorcycle&) {};
  virtual void Visit(class Plane&) {};
  virtual void Visit(class Car&) {};
};

class Vehicle {
 public:
  virtual Accept(VehicleVisitor&) = 0;
};

class Car : public Vehicle {
 public:
  virtual Accept(VehicleVisitor& pVisitor) {
   pVisitor.Visit(*this);
  }
};

// and so on...

В какой-то момент вашей программы вам нужно получить все экземпляры, скажем, Motorcycle:

class MotorcycleExtractingVisitor : public VehicleVisitor {
  std::vector<Motorcycle*> mMotorcycles;
 public: 

  void operator()(Vehicle* pVehicle) {
   pVehicle->Accept(*this);
  }

  void Visit(Motorcycle& pMotorcycle) {
   mAllMotorcycles.push_back(pMotorcycle);
  }

  std::vector<Motorcycles*> Get() { return mAllMotorcycles; }
};

class Extractor {
public:

  // here you extract motorcycles
  static std::vector<Motorcycle*> ExtractMotorcycles(std::vector<Vehicle*>& pVehicles) {
   MotorcycleExtractingVisitor tMotos;
   std::for_each(pVehicles.begin(), pVehicles.end(), tMotos);
   return tMotos.Get();
  }

  // this would be a templatized version, left as exercise to the reader
  template<class TExtracted, classtypename TBegItr, typename TEndItr>
  static std::vector<TExtracted*> Extract(TBegItr pBeg, TEndItr pEnd) {
   ExtractingVisitor<TExtracted> tRequiredVehicles;
   std::for_each(pBeg, pEnd, tRequiredVehicles);
   return tRequiredVehicles.Get();
  }

};

Использование выглядит следующим образом:

// fixed type version:
std::vector<Motorcycles*> tMotos =
  Extractor::Extract(tVehicleVector);

// templatized version (recommended)
std::vector<Motorcycles*> tMotos = 
  Extractor::Extract<Motorcycles>(
    tVehicleVector.begin(),tVehicleVector.end());
0 голосов
/ 17 октября 2010

dynamic_cast проверит сам тип (для этого вам не нужна ваша собственная переменная).Вместо этого вы можете сделать следующее:

Plane *plane_ptr = dynamic_cast<Plane*>(vehicle_ptr);
if(plane_ptr != NULL)
{
    // Do stuff with 'plane_ptr' that you couldn't do with 'vehicle_ptr'
}

Я не очень понимаю, как может помочь создание функции для приведения, потому что вам все равно нужно классифицировать определенный код (а функция будет иметь фиксированный возврат)type, так что вы можете получить что-то похожее на вызов dynamic_cast, который в любом случае является довольно стандартной функцией).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...