在面向对象的继承中使用协方差避免冗余代码

时间:2016-06-23 10:33:40

标签: c++11 inheritance subclass covariance redundancy

我知道之前可能已经问过这个问题,但我真的不知道要搜索什么,因为我甚至不确定我选择的标题。

我想要实现的目标: 如果我使用 getMeasurement(int timestep)并且传感器对象的地图中没有包含Integer值,则应使用特定于测量的方法 interpolate(。)插值(如果可能)。 ..)。 它应该从地图中获取正确的两个Measurements对象,用于超类Sensor中的插值并插入它们,但我不知道如何以及是否可以调用插值。也许使用Generics / Typename或设计模式。

Sensor someSensor = ...
Measurement measurementAt2 = someSensor.getMeasurement(2); 
// should interpolate value if map measurements in someSensor not has the key 2

其他信息: ASensor ::测量只包含 AMeasurements BSensor :: measurements 只包含 BMeasurements 。 ......这些测量包含不同的值类型,因此每个测量子类都需要以不同方式进行插值。

abstract class Sensor {
   map<int, Measurement> measurements;
   Measurement getMeasurement(int timestep);
}

class ASensor : Sensor {
   ...
}

class BSensor : Sensor {
   ...
}


abstract class Measurement {
   ...
}

class AMeasurement : Measurement {
   AMeasurement interpolate(AMeasurement other, int timestep);
}

class BMeasurement : Measurement {
   BMeasurement interpolate(BMeasurement other, int timestep);
}

如果我在Measurement for inheritance中添加一个抽象/虚拟方法 Measurement interpolated(Measurement other,int timestep),这个签名对子类不好,因为我需要检查类类型并投下另一个测量。

我很感激能够回答C ++ 11中的代码,我目前正在使用它。

编辑:如果需要此信息,子类ASensor + AMeasurement,BSensor + BMeasurement,...都是独立加载的插件。

EDIT2:添加了方法的返回类型(我忘了)。

3 个答案:

答案 0 :(得分:0)

我不完全确定你的问题,但在c ++中我可以获得返回值协方差。也许这样的事情可以帮到你(见评论):

#include <iostream>
using namespace std;

struct Measurement //Abstract
{
    virtual int apply(int timestep) const = 0;
    virtual std::string name() const = 0;
  protected:
    virtual ~Measurement(){}
  //...virtual Meas... etc
};

struct AMeasurement : Measurement //Implements Measurement for sensorA
{
  std::string name() const override{ return "AMeasurement"; }
  int apply(int timestep) const override
  {
    return timestep * 10;
  }
};
struct BMeasurement : Measurement //Implements Measurement for sensorB
{
  std::string name() const override{ return "BMeasurement"; }
  int apply(int timestep) const override
  {
    return timestep * 20;
  }
};

struct MeasurementProvider //Provides measurement
{
  virtual const Measurement& getMeasurement() const = 0;
  //...etc
  protected:
    virtual ~MeasurementProvider(){}
};

//Generalized measurement provider.
// Covariance ensure correct measurement used. Currently most basic
// implementation. Can elaborate
template <class MeasurementType>
struct GenMeasurementProvider : MeasurementProvider
{
  //NOTE: MeasureType derived from Measurement, hence covariance...
  const MeasurementType& getMeasurement() const override{return m_;}
  MeasurementType m_;
};

// Perhaps Sensor is just a generalized Provider.
struct SensorA : GenMeasurementProvider<AMeasurement>
{
};

// Interpolate using provider instead of actual measurement to
// allow for covariance.
void interpolate(const MeasurementProvider& provider, int timestep)
{
  //return type allows covariance, therefore apply to be
  // called on correct type
  auto const& measurement = provider.getMeasurement();

  std::cout << "Result of measurement " << measurement.name() 
            << ":" << measurement.apply(timestep) << std::endl;
}

int main() {
  const int timestep = 100;
  interpolate(GenMeasurementProvider<AMeasurement>{}, timestep);
  interpolate(GenMeasurementProvider<BMeasurement>{}, timestep);
  interpolate(SensorA{}, timestep);
  return 0;
}

我已经省略了许多细节,我可能会更清晰地着色。

答案 1 :(得分:0)

如果您的问题是interpolate()的签名,我想您可以根据派生类转换模板类中的Measurement;

之类的东西
template <typename Derived>
class Measurement {
   Derived interpolate (Derived other, int timestep)
    { /* do something */ }
};

class AMeasurement : Measurement<AMeasurement> {
   // ...
};

class BMeasurement : Measurement<BMeasurement> {
   // ...
};

p.s:抱歉我的英语不好

答案 2 :(得分:0)

如果您在getMeasurement纯虚拟中使用Sensor函数,那么您就不需要基类中的map。然后由Sensor的实现来存储它们自己类型的测量值并在它们之间进行插值。您可以提供模板类来完成所有工作:

class Sensor {
   public:
     virtual std::unique_ptr<Measurement> getMeasurement(int timestep) const = 0;
};

template<typename M>
class BasicSensor : public Sensor {
    std::map<int, M> measurements;
  public:
    std::unique_ptr<Measurement> getMeasurement(int timestep) const override {
        auto itr = measurements.lower_bound(timestep);

        if (itr == measurements.end())  // Cant find measurement equal or later
            return nullptr;             // than timestep so can't interpolate.

        if (itr->first == timestep)                   // Found exact match so
            return std::make_unique<M>(itr->second);  // don't need to interpolate.

        if (itr == measurements.begin()) // Can't find measurement before
            return nullptr;              // timestep so can't interpolate.

        auto interpolated = std::prev(itr)->second.interpolate(itr->second, timestep);

        // Copy to smart-pointer to avoid slicing
        return std::make_unique<M>(interpolated); 
    }
    void setMeasurement(int timestep, const M& m) {
        measurements[timestep] = m;
    }
};

class ASensor : public BasicSensor<AMeasurement> {};
class BSensor : public BasicSensor<BMeasurement> {};

Live demo