我只是在学习C ++和编程。我正在创建一个名为 Distance 的类。我想允许用户(程序员使用),我的班级能够将距离从一个度量单位转换为另一个度量单位。例如:英寸 - >厘米,英里 - >公里等...

我的问题是我想要一个名为 ConvertTo 的方法,它将转换为任何度量单位。

这是我到目前为止所拥有的:

// unit_of_measure is an enum containg all my supported lengths,
// (eg. inches, centimeters, etc...)
int Distance::ConvertTo(unit_of_measure convert_unit)
{
    switch (convert_unit)
    {
        case inches:
            if (unit != inches) {
                if (unit == centimeters) {
                    distance *= CM_TO_IN;
                    unit = inches;
                    return 0;
                } else {
                    cerr << "Conversion not possible (yet)." << endl;
                    return 1;
                }
            } else {
                cout << "Warning: Trying to convert inches to inches." << endl;
                return 2;
            }
        case centimeters:
            if (unit != centimeters) {
                if (unit == inches) {
                    distance /= CM_TO_IN;
                    unit = centimeters;
                    return 0;
                } else {
                    cerr << "Conversion not possible (yet)." << endl;
                    return 1;
                }
            } else {
                cout << "Warning: Trying to convert inches to inches." << endl;
                return 2;
            }
// I haven't written anything past here yet because it seems
// like a bad idea to keep going with this huge switch 
// statement.
        default:
            cerr << "Undefined conversion unit." << endl;
            return -1;
    }
}

那我该怎么办?我应该解决这个问题,还是继续使用 HUGE 切换语句。

有帮助吗?

解决方案

将其分解为各种功能。你所拥有的东西将很难维持使用。用户和程序员使用具有描述性名称的函数会更方便:

double inchesToCentimeters(double inches);
double centimetersToInches(double cent);

函数名称确切地告诉您要调用的函数,并且不需要传递跟踪单位的额外参数。

除此之外,为了防止必须跟踪测量所在的单位,最好始终将数字存储在程序中的任何位置的公共单元中,然后仅在将数字转换为显示单位时你需要。例如,我现在维护的程序将所有距离值保持为米,但可以转换为您能想到的任何距离单位。

使用通用装置时,可以节省大量的功能。假设您的公共距离单位是米,现在一旦您编写从米转换到您需要的每个其他单位的功能,并且从所有其他单位转换为米,您可以将它们组合起来从任何单位 - 到米 - 到任何其他单位

其他提示

我的方法是始终将距离存储在同一个单元中。这样可以避免在需要转换值时始终仔细检查单位。

代码的骨架可能如下所示:

class Distance
{
public:

   float ConvertTo (unit_of_measure convert_unit)
   {
      return (_distanceInInches * getConversionFactor(convert_unit));
   }

   float SetValue (unit_of_measure unit, float value)
   {
      _distanceInInches = (value / getConversionFactor(unit));
   }

private:   

   float getConversionFactor(unit_of_measure unit)
   {
      switch(unit)
      {
         // add each conversion factor here
      }
   }

   float _distanceInInches;
}

如果您不介意依赖,请使用 Boost.Units

如果您想要准确保留当前的API,但要简化其实施,为什么不能根据某些任意标准(例如1米)来表示您的设备。至少,不是具有N ^ 2(source-&gt; dest)可能性,而是具有2 * N(source-&gt; std)(std-> dest)转换。

struct distance_unit {
   char const* name;
   double meters_per_unit;
   distance_unit() : name("meters"),meters_per_unit(1.) {}
   double to_meters(double in_units) { return in_units/meters_per_unit; }
   double to_units(double in_meters) { return in_meters*meters_per_unit; }
};

struct distance {
   double d;
   distance_unit unit;
   distance(double d,distance_unit const& unit) : d(d),unit(unit) {}
   distance(double meters,distance_unit const& unit,bool _)
      : d(unit.to_units(meters)),unit(unit) {}
   distance convert_to(distance_unit const& to) {
        return distance(unit.to_meters(d),to,false);
   }
   friend inline std::ostream& operator<<(std::ostream &o) {
      return o << d << ' ' << unit.name;
   }
};

当然,唯一的好处是可以准确表示的距离(就其单位而言)不会变得不精确。如果你不关心舍入和完全相等的和,这是更明智的:

struct distance {
   double meters;
   distance_unit preferred_unit;
   distance(double d,distance_unit const& unit) 
     : meters(unit.to_meters(d)),preferred_unit(unit) {}
   distance(double meters,distance_unit const& unit,bool _)
     : meters(meters),preferred_unit(unit)
   distance convert_to(distance_unit const& to) { 
       return distance(meters,to,false);
   }
   friend inline std::ostream& operator<<(std::ostream &o) {
      return o << unit.to_units(meters) << ' ' << unit.name;
   }

};

如果使用STL,请创建转换图的常量图。因此,您可以从“从”获得转换常量和“到”。

这样的事情:

std::map <unit_of_measure, std::map<unit_of_measure, double>> ConversionConstants_FromTo;

ConversionConstants_FromTo(inches)(centimeters) = ...;
ConversionConstants_FromTo(inches)(miles)       = ...;

int Distance::ConvertTo(unit_of_measure convert_unit) {
    return distance*ConversionConstants_FromTo(unit, convert_unit)
}

我会进行两个级别的分析。

首先是您为来电者提供的界面。调用者创建一个具有特定单位的Distance对象,然后convert方法更改单位和相应的距离,错误代码表示成功或否则。据推测,你也可以获得当前单位和相应的距离。

现在我不喜欢这样一个有状态的界面。为什么不是像

这样的界面
 Distance {

      Distance(unit, value) { // constructor

      float getValue(unit) throws UnsupportedUnitException;
 }

因此呼叫者无需了解距离的内部单位。没有有状态的行为。

然后switch语句显然是重复的。这必须是一些重构的候选人。

每次转换都可以表示为乘法。您可以拥有一个表,保留您支持的所有转换因子。你有

       float getConversionFactor(fromUnit, toUnit) throws UnsupportedUnitException

执行转换因子的查找,然后将其应用于getValue()方法

       getValue(requestedUnit) {
             return value * getConversionfactor(myUnit, requestedUnit);
       }

当你还在学习时,可能值得放弃使用switch和enum方法来支持一个结构体系列,每个处理单元一个,每个包含输入值和唯一标记类型以使它们不同。这有一些好处:

  1. 转换可以通过具有通用名称(“转换”可能?)的函数的重载来完成,如果您想在模板化代码中进行转换,这会使生活更轻松。
  2. 类型系统意味着您不会意外地将加仑转换为光年,因为您不会编写编译器可能与尺寸不合适的转换匹配的重载。
  3. 交换机的执行时间或嵌套,如果块取决于子句的数量,则在编译时切换重载方法
  4. 缺点是创建该类标记类的开销以及需要在类中包含您的参数(更可能是struct)。一旦被包装,除非你编写它们,否则你将没有通常的数字运算符。

    一种值得学习的技术imo。

这里还有两件事需要考虑,因为这里已经有很多非常好的想法。

(1)如果你不打算将长度表示为值类型,那么我将使用一个充满自由函数而不是类的命名空间。这更像是我喜欢传播的风格 - 如果您没有状态或正在考虑 static 方法,只需使用命名空间。

namespace Convert {
  double inchesToCentimeters(double inches) { ... }
  double inchesToMeters(double inches) { ... }
} // end Convert namespace

(2)如果您打算使用值类型(这是我推荐的),那么考虑(我一直在调用的)“命名构造函数”而不是单位枚举以及单个单位表示。

class Convert {
public:
  static Convert fromInches(double inches) {
      return Convert(inches * 0.0254);
  }
  static Convert fromCentimeters(double cm) {
      return Convert(cm / 100.0);
  }
  ...
  double toInches() const { return meters * 39.370079; }
  double toCentimeters() const { return meters * 100.0; }
  ...
protected:
  Convert(double meters_): meters(meters_) {}
private:
  double meters;
};

这将使您的用户土地代码非常易读,您可以从选择任何内部单元使您的生活变得轻松中获益。

在源代码中更容易阅读单独的方法,但是当目标单元出现时,例如从用户选择中,您需要一个可以将其指定为参数的函数。

您可以使用缩放因子表,而不是switch语句,例如特定单位和国际单位制之间。

另外,请查看 Boost.Units ,它并没有完全解决你的问题,但它足够接近有趣。

我会把NawaMan和ph0enix的答案结合起来。而不是拥有地图地图,只需要有2个完整常量的地图。一个地图包含来自米的转换,另一个地图包含反转。然后该函数类似于(在伪代码中):

function convertTo (baseUnitName, destinationUnitName) {
    let A = getInverseConstant(baseUnitName);
    let B = getConstant(destinationUnitName);

    return this.baseUnit*A*B;
}

这比你的山区切换开关语句短得多,并且我认为两个完整常量的映射比switch语句更容易维护。地图图基本上只是一个时间表,所以为什么不存储垂直和水平的cooeficients而不是n * m的内存块。

您甚至可以编写代码来读取文本文件中的常量,然后生成每个值上带有1 / x的反常数。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top