用于在C ++ std :: <vector>容器</vector>中存储多种类型结构的模式

时间:2011-10-11 06:52:23

标签: c++ design-patterns

我有一个代表火车的数据结构,可以由多种类型的汽车组成,例如火车引擎,谷物汽车,乘用车等等:

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方法?

3 个答案:

答案 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的设计 正是这个成语。

相关问题