我有一个代表火车的数据结构,可以由多种类型的汽车组成,例如火车引擎,谷物汽车,乘用车等等:
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
}
}
我不确定这种模式(使用dynamic_casts和if语句链)是否是处理不同类型的简单结构列表的最佳方法。使用dynamic_cast似乎不正确。
一种选择是将路由逻辑移动到结构(就像(* it) - &gt; route(is_start_of_car,&amp; some_other_data_structure ...)),但是我想将路由逻辑保持在一起,如果可能的。
有没有更好的方法来迭代不同类型的简单结构(没有方法)?或者我是否保留dynamic_cast方法?
答案 0 :(得分:8)
此标准解决方案称为double-dispatch。基本上,您首先将算法包装在为每种类型的汽车重载的单独函数中:
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
。
答案 1 :(得分:0)
如果课程数量可以管理,您可以尝试boost::variant
。
在C ++中使用“sum types”有时候很乱,所以要么是这个要么是双重调度。
答案 2 :(得分:0)
经典的OO解决方案是完成所有相关功能
在基类TrainCar
中为virtual,并在每个中添加具体逻辑
类。但是,你说你想保留路由逻辑
如果可能的话。有些情况下这是合理的,而且
在这种情况下的经典解决方案是变体联合(boost::variant
,
例如)。您可以自行决定哪种情况更好。
也可以妥协。例如,人们很容易想象出一个
路由逻辑在某种程度上与汽车类型无关的情况
(并且您不希望在每种车型中复制它),但确实如此
取决于汽车类型的一定数量的特征。在这
例如,TrainCar
中的虚函数可以简单地返回一个对象
具有必要的依赖信息,由路由使用
算法。该解决方案具有减少耦合的优点
在路由和TrainCar
之间达到最低要求。
取决于此信息的性质及其信息
使用后,返回的对象可以是多态的,具有继承性
反映TrainCar
的层次结构;在这种情况下,它必须是
动态分配和托管:std::auto_ptr
的设计
正是这个成语。