Шаблон для хранения нескольких типов структур в контейнере C ++ std :: <vector> - PullRequest
4 голосов
/ 11 октября 2011

У меня есть структура данных, которая представляет поезд, который может состоять из многих типов вагонов, например, железнодорожных двигателей, вагонов, пассажирских вагонов и т. Д.

struct TrainCar {
   // ...
   Color color;
   std::string registration_number;
   unsigned long destination_id;
}

struct PowerCar : TrainCar {
   // ...
   const RealPowerCar &engine;
}

struct CargoCar : TrainCar {
   // ...
   const RealCargoCar &cargo;
   bool full;
}

std::vector<TrainCar*> cars;
cars.push_back(new TrainCar(...));
cars.push_back(new TrainCar(...));
cars.push_back(new CargoCar(...));
cars.push_back(new CargoCar(...));
cars.push_back(new CargoCar(...));

Алгоритм будет перебирать вагоны в поезде и решать, как прокладывать / шунтировать каждый вагон (следует ли оставить его в поезде, переместить его в другую точку поезда, удалить из поезда). Этот код выглядит так:

std::vector<TrainCar*>::iterator it = cars.begin();
for (; it != cars.end(); ++it) {
    PowerCar *pc = dynamic_cast<PowerCar*>(*it);
    CargoCar *cc = dynamic_cast<CargoCar*>(*it);

    if (pc) {
        // Apply some PowerCar routing specific logic here
        if (start_of_train) {
            // Add to some other data structure
        }
        else if (end_of_train && previous_car_is_also_a_powercar) {
            // Add to some other data structure, remove from another one, check if something else...
        }
        else {
            // ...
        }
    }
    else if (cc) {
        // Apply some CargoCar routing specific logic here
        // Many business logic cases here
    }
}

Я не уверен, является ли этот шаблон (с динамическими_кастами и цепочкой операторов if) лучшим способом для обработки списка простых структур различных типов. Использование dynamic_cast кажется некорректным.

Один из вариантов - переместить логику маршрутизации в структуры (например, (* it) -> route (is_start_of_car, & some_other_data_structure ...)), однако я хотел бы сохранить логику маршрутизации, если это возможно.

Есть ли лучший способ перебора различных типов простой структуры (без методов)? Или я придерживаюсь метода dynamic_cast?

Ответы [ 3 ]

8 голосов
/ 11 октября 2011

Стандартное решение для этого называется двойная отправка .По сути, вы сначала оборачиваете свои алгоритмы в отдельные функции, перегруженные для каждого типа автомобиля:

void routeCar(PowerCar *);
void routeCar(CargoCar *);

Затем вы добавляете метод route к автомобилю, который является чисто виртуальным в базовом классе, иреализован в каждом из подклассов:

struct TrainCar {
   // ...
   Color color;
   std::string registration_number;
   unsigned long destination_id;

   virtual void route() = 0;
}

struct PowerCar : TrainCar {
   // ...
   const RealPowerCar &engine;
   virtual void route() {
       routeCar(this);
   }
}

struct CargoCar : TrainCar {
   // ...
   const RealCargoCar &cargo;
   bool full;
   virtual void route() {
       routeCar(this);
   }
}

Ваш цикл выглядит следующим образом:

std::vector<TrainCar*>::iterator it = cars.begin();
for (; it != cars.end(); ++it) {
    (*it)->route();
}

Если вы хотите выбирать между различными алгоритмами маршрутизации во время выполнения, вы можете обернутьrouteCar -функции в абстрактном базовом классе и предоставляют различные реализации для этого.Затем вы должны передать соответствующий экземпляр этого класса в TrainCar::route.

0 голосов
/ 11 октября 2011

Классическим решением ОО было бы сделать все соответствующие функции виртуальными в базовом классе TrainCar и поместить конкретную логику в каждый класс.Однако вы говорите, что хотели бы сохранить логику маршрутизации, если это возможно.Есть случаи, когда это оправдано, и классическим решением в таких случаях является вариант объединения (например, boost::variant).Вам решать, что лучше в вашем случае.

Возможны и компромиссы.Например, можно легко представить себе случай, когда логика маршрутизации несколько не зависит от типа автомобиля (и вы не хотите дублировать ее в каждом типе автомобиля), но она зависит от определенного количества характеристик типа автомобиля.,В этом случае виртуальная функция в TrainCar может просто возвращать объект с необходимой зависимой информацией, которая будет использоваться алгоритмом маршрутизации.Преимущество этого решения заключается в уменьшении связи между маршрутизацией и TrainCar до необходимого минимума.

В зависимости от характера этой информации и способа ее использования, возвращаемый объект может быть полиморфным с егоиерархия наследования, отражающая иерархию TrainCar;в этом случае он должен быть распределен динамически и управляться: std::auto_ptr был разработан с учетом именно этой идиомы.

0 голосов
/ 11 октября 2011

Если количество классов является управляемым, вы можете попробовать boost::variant.

Использование «типов сумм» в C ++ иногда приводит к путанице, так что это либо двойная диспетчеризация.

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