受早期问题的启发,我制作了一个温度转换器,它可以从一种温度转换到另一种温度,并可选择性地输出一系列转换。
这是在Visual 2015中完成的,但它也使用Debian上的g++和clang++进行编译,没有警告。
tc.exe -40 c f
Celcius | Fahrenheit
------------------+------------------
-40.00 | -40.00或
tc.exe -40 c f 10 10
Celcius | Fahrenheit
------------------+------------------
-40.00 | -40.00
-30.00 | -22.00
-20.00 | -4.00
-10.00 | 14.00
0.00 | 32.00
10.00 | 50.00如果论点是:
Usage: tc.exe Degrees InitialUnit ConvertedUnit [InitialUnitStepSize InitialUnitUpperBoundInclusive]#pragma once
#include <stdio.h>
#include <string>
#include <cstring>
#include <sstream>
#include <memory>
#include <iostream>
#include <limits>
#include <cmath>#pragma once
#include "stdafx.h"
namespace hest {
enum class TemperatureUnit
{
Invalid,
Celcius,
Fahrenheit,
Kelvin,
Rankine,
};
class TemperatureData;
class Kelvin;
class Temperature {
public:
typedef std::unique_ptr<Temperature> UniqueTemperature;
static std::string ToShortString(TemperatureUnit const unit);
static std::string ToString(TemperatureUnit const unit);
static TemperatureUnit StringToUnit(std::string const & unit);
static TemperatureUnit CharToUnit(char unit);
static UniqueTemperature CreateTemperature(double degrees, TemperatureUnit unit);
static UniqueTemperature Convert(Temperature const & from, TemperatureUnit const to);
double degrees() const;
TemperatureUnit unit() const;
std::string ToString() const;
virtual Kelvin ToKelvin() const = 0;
protected:
Temperature(double degrees, TemperatureUnit unit);
private:
std::shared_ptr<TemperatureData> data_;
};
class Kelvin final : public Temperature {
public:
Kelvin(double degrees);
Kelvin ToKelvin() const;
UniqueTemperature ConvertTo(TemperatureUnit const unit) const;
};
class Celcius final : public Temperature {
public:
Celcius(double degrees);
Kelvin ToKelvin() const;
};
class Rankine final : public Temperature {
public:
Rankine(double degrees);
Kelvin ToKelvin() const;
};
class Fahrenheit final : public Temperature {
public:
Fahrenheit(double degrees);
Kelvin ToKelvin() const;
};
inline bool operator==(Kelvin const & lhs, Kelvin const & rhs) { return lhs.degrees() == rhs.degrees(); }
inline bool operator!=(Kelvin const & lhs, Kelvin const & rhs) { return !operator==(lhs, rhs); }
inline bool operator< (Kelvin const & lhs, Kelvin const & rhs) { return lhs.degrees() < rhs.degrees(); }
inline bool operator> (Kelvin const & lhs, Kelvin const & rhs) { return operator< (rhs, lhs); }
inline bool operator<=(Kelvin const & lhs, Kelvin const & rhs) { return !operator> (lhs, rhs); }
inline bool operator>=(Kelvin const & lhs, Kelvin const & rhs) { return !operator< (lhs, rhs); }
inline bool operator==(Temperature const & lhs, Temperature const & rhs) { return lhs.ToKelvin() == rhs.ToKelvin(); }
inline bool operator!=(Temperature const & lhs, Temperature const & rhs) { return !operator==(lhs, rhs); }
inline bool operator< (Temperature const & lhs, Temperature const & rhs) { return lhs.ToKelvin() < rhs.ToKelvin(); }
inline bool operator> (Temperature const & lhs, Temperature const & rhs) { return operator< (rhs, lhs); }
inline bool operator<=(Temperature const & lhs, Temperature const & rhs) { return !operator> (lhs, rhs); }
inline bool operator>=(Temperature const & lhs, Temperature const & rhs) { return !operator< (lhs, rhs); }
inline Kelvin operator+(Kelvin const & lhs, double rhs) { return Kelvin(lhs.degrees() + rhs); }
inline Kelvin operator+(Kelvin const & lhs, Kelvin const & rhs) { return Kelvin(lhs.degrees() + rhs.degrees()); }
inline Kelvin operator-(Kelvin const & lhs, double rhs) { return Kelvin(lhs.degrees() - rhs); }
inline Kelvin operator-(Kelvin const & lhs, Kelvin const & rhs) { return Kelvin(lhs.degrees() - rhs.degrees()); }
inline Temperature::UniqueTemperature operator+(Temperature const & lhs, double rhs) {
return Temperature::CreateTemperature(lhs.degrees() + rhs, lhs.unit());
}
inline Temperature::UniqueTemperature operator+(Temperature const & lhs, Temperature const & rhs) {
if (lhs.unit() == rhs.unit()) {
return Temperature::CreateTemperature(lhs.degrees() + rhs.degrees(), lhs.unit());
}
auto converted = Temperature::Convert(lhs, rhs.unit());
return Temperature::CreateTemperature(rhs.degrees() + converted->degrees(), rhs.unit());
}
inline Temperature::UniqueTemperature operator-(Temperature const & lhs, double rhs) {
return Temperature::CreateTemperature(lhs.degrees() - rhs, lhs.unit());
}
inline Temperature::UniqueTemperature operator-(Temperature const & lhs, Temperature const & rhs) {
if (lhs.unit() == rhs.unit()) {
return Temperature::CreateTemperature(lhs.degrees() - rhs.degrees(), lhs.unit());
}
auto converted = Temperature::Convert(lhs, rhs.unit());
return Temperature::CreateTemperature(rhs.degrees() - converted->degrees(), rhs.unit());
}
}#include "stdafx.h"
#include "temperature.h"
namespace hest {
constexpr double kCelciusToKelvinOffset = 273.15;
constexpr double kFahrenheitToRankineOffset = 459.67;
static inline double RankineToKelvin(double rankineDegrees) {
return rankineDegrees * 5.0 / 9.0;
}
static inline double KelvinToRankine(double kelvinDegrees) {
return kelvinDegrees * 9.0 / 5.0;
}
class TemperatureData {
private:
double const degrees_;
TemperatureUnit const unit_;
public:
TemperatureData(double degrees, TemperatureUnit unit) : degrees_(degrees), unit_(unit) {
if (!std::isfinite(degrees)) {
throw std::invalid_argument("Degrees must be finite");
}
if (unit == TemperatureUnit::Invalid) {
throw std::invalid_argument("Invalid unit");
}
}
double degrees() const { return degrees_; }
TemperatureUnit unit() const { return unit_; }
};
Temperature::Temperature(double degrees, TemperatureUnit unit) : data_(std::make_shared<TemperatureData>(degrees, unit)) {}
Kelvin::Kelvin(double degrees) : Temperature(degrees, TemperatureUnit::Kelvin) { }
Celcius::Celcius(double degrees) : Temperature(degrees, TemperatureUnit::Celcius) { }
Rankine::Rankine(double degrees) : Temperature(degrees, TemperatureUnit::Rankine) { }
Fahrenheit::Fahrenheit(double degrees) : Temperature(degrees, TemperatureUnit::Fahrenheit) { }
Kelvin Kelvin::ToKelvin() const {
return Kelvin(degrees());
}
Kelvin Celcius::ToKelvin() const {
return Kelvin(degrees() + kCelciusToKelvinOffset);
}
Kelvin Rankine::ToKelvin() const {
return Kelvin(RankineToKelvin(degrees()));
}
Kelvin Fahrenheit::ToKelvin() const {
return Kelvin(RankineToKelvin(degrees() + kFahrenheitToRankineOffset));
}
std::string Temperature::ToShortString(TemperatureUnit const unit)
{
switch (unit)
{
case hest::TemperatureUnit::Kelvin:
return "K";
case hest::TemperatureUnit::Celcius:
return "C";
case hest::TemperatureUnit::Rankine:
return "R";
case hest::TemperatureUnit::Fahrenheit:
return "F";
default:
return "";
}
}
std::string Temperature::ToString(TemperatureUnit const unit)
{
switch (unit)
{
case hest::TemperatureUnit::Kelvin:
return "Kelvin";
case hest::TemperatureUnit::Celcius:
return "Celcius";
case hest::TemperatureUnit::Rankine:
return "Rankine";
case hest::TemperatureUnit::Fahrenheit:
return "Fahrenheit";
default:
return "";
}
}
TemperatureUnit Temperature::StringToUnit(std::string const & unit) {
auto length = unit.length();
switch (length)
{
case 1:
return CharToUnit(unit[0]);
case 6:
if (unit.compare("Kelvin") == 0 || unit.compare("kelvin") == 0) {
return TemperatureUnit::Kelvin;
}
case 7:
if (unit.compare("Celcius") == 0 || unit.compare("celcius") == 0) {
return TemperatureUnit::Celcius;
}
if (unit.compare("Rankine") == 0 || unit.compare("rankine") == 0) {
return TemperatureUnit::Rankine;
}
case 10:
if (unit.compare("Fahrenheit") == 0 || unit.compare("fahrenheit") == 0) {
return TemperatureUnit::Fahrenheit;
}
}
return TemperatureUnit::Invalid;
}
TemperatureUnit Temperature::CharToUnit(char unit) {
switch (unit)
{
case 'c':
case 'C':
return TemperatureUnit::Celcius;
case 'f':
case 'F':
return TemperatureUnit::Fahrenheit;
case 'k':
case 'K':
return TemperatureUnit::Kelvin;
case 'r':
case 'R':
return TemperatureUnit::Rankine;
default:
return TemperatureUnit::Invalid;
}
}
auto Temperature::CreateTemperature(double degrees, TemperatureUnit unit) -> UniqueTemperature {
switch (unit)
{
case hest::TemperatureUnit::Kelvin:
return std::make_unique<Kelvin>(degrees);
case hest::TemperatureUnit::Celcius:
return std::make_unique<Celcius>(degrees);
case hest::TemperatureUnit::Rankine:
return std::make_unique<Rankine>(degrees);
case hest::TemperatureUnit::Fahrenheit:
return std::make_unique<Fahrenheit>(degrees);
default:
throw std::invalid_argument("Invalid unit");
}
}
auto Temperature::Convert(Temperature const & from, TemperatureUnit const to) -> UniqueTemperature {
auto kelvin = from.ToKelvin();
return kelvin.ConvertTo(to);
}
double Temperature::degrees() const {
return data_->degrees();
}
TemperatureUnit Temperature::unit() const {
return data_->unit();
}
std::string Temperature::ToString() const {
std::ostringstream format_stream;
format_stream << degrees() << ToShortString(unit());
return format_stream.str();
}
auto Kelvin::ConvertTo(TemperatureUnit const unit) const -> UniqueTemperature {
switch (unit)
{
case hest::TemperatureUnit::Kelvin:
return std::make_unique<Kelvin>(degrees());
case hest::TemperatureUnit::Celcius:
return std::make_unique<Celcius>(degrees() - kCelciusToKelvinOffset);
case hest::TemperatureUnit::Rankine:
return std::make_unique<Rankine>(KelvinToRankine(degrees()));
case hest::TemperatureUnit::Fahrenheit:
return std::make_unique<Fahrenheit>(KelvinToRankine(degrees()) - kFahrenheitToRankineOffset);
default:
throw std::invalid_argument("Invalid unit");
}
}
}#include "stdafx.h"
#include "temperature.h"
#include <iomanip>
constexpr int kMaxDigits = std::numeric_limits<double>::digits10;
constexpr int kColumnWidth = kMaxDigits + 2;
constexpr auto kRowSeparator = " | ";
static const auto kRowSeparatorLength = std::strlen(kRowSeparator);
std::string PrintResultHeader(hest::TemperatureUnit const initial, hest::TemperatureUnit const converted) {
std::ostringstream format_stream;
format_stream << std::left;
format_stream << std::setw(kColumnWidth) << hest::Temperature::ToString(initial);
format_stream << std::setw(0) << kRowSeparator;
format_stream << std::setw(kColumnWidth) << hest::Temperature::ToString(converted);
auto column_header_separator = std::string(kColumnWidth + kRowSeparatorLength / 2, '-');
format_stream << std::setw(0) << "\n" << column_header_separator;
if (kRowSeparatorLength % 2 == 1) {
format_stream << "+";
}
format_stream << column_header_separator;
return format_stream.str();
}
std::string PrintResultRow(hest::Temperature::UniqueTemperature const & initial, hest::Temperature::UniqueTemperature const & converted) {
std::ostringstream format_stream;
format_stream << std::right << std::setprecision(2) << std::fixed;
format_stream << std::setw(kColumnWidth) << initial->degrees();
format_stream << std::setw(0) << kRowSeparator;
format_stream << std::setw(kColumnWidth) << converted->degrees();
return format_stream.str();
}
template<typename TResult> bool TryParse(std::string const & input, TResult & result) {
std::istringstream input_stream(input);
input_stream >> result;
return !input_stream.fail() && input_stream.eof();
}
int main(int argc, char* argv[]) {
if (!(argc == 4 || argc == 6)) {
std::cout << "Usage: " << argv[0] << " Degrees InitialUnit ConvertedUnit [InitialUnitStepSize InitialUnitUpperBoundInclusive]" << std::endl;
return EXIT_FAILURE;
}
bool has_errors = false;
auto degrees_argument = std::string(argv[1]);
double degrees = 0;
if (!TryParse(degrees_argument, degrees)) {
std::cerr << "Degrees: " << degrees_argument << " is not a valid number" << std::endl;
has_errors = true;
}
auto initial_unit_argument = std::string(argv[2]);
auto initial_unit = hest::Temperature::StringToUnit(initial_unit_argument);
if (initial_unit == hest::TemperatureUnit::Invalid) {
std::cerr << "InitialUnit: " << initial_unit_argument << " is not a valid unit" << std::endl;
has_errors = true;
}
auto converted_unit_argument = std::string(argv[3]);
auto converted_unit = hest::Temperature::StringToUnit(converted_unit_argument);
if (converted_unit == hest::TemperatureUnit::Invalid) {
std::cerr << "ConvertedUnit: " << converted_unit_argument << " is not a valid unit" << std::endl;
has_errors = true;
}
/* step_size and upper_bound are initialized such that if they're not specified
the conversion loop will run exactly once. */
double step_size = std::numeric_limits<double>::max();
double upper_bound = degrees;
if (argc == 6) {
auto step_size_argument = std::string(argv[4]);
if (!TryParse(step_size_argument, step_size)) {
std::cerr << "InitialUnitStepSize: " << step_size_argument << " is not a valid number" << std::endl;
has_errors = true;
}
if (!has_errors && step_size <= 0) {
std::cerr << "InitialUnitStepSize must be positive" << std::endl;
has_errors = true;
}
auto upper_bound_argument = std::string(argv[5]);
if (!TryParse(upper_bound_argument, upper_bound)) {
std::cerr << "InitialUnitUpperBoundInclusive: " << upper_bound_argument << " is not a valid number" << std::endl;
has_errors = true;
}
if (!has_errors && upper_bound <= 0) {
std::cerr << "InitialUnitUpperBoundInclusive must be greater than Degrees" << std::endl;
has_errors = true;
}
}
if (has_errors) {
return EXIT_FAILURE;
}
try
{
std::cout << PrintResultHeader(initial_unit, converted_unit) << std::endl;
for (auto initial_temperature = hest::Temperature::CreateTemperature(degrees, initial_unit);
initial_temperature->degrees() <= upper_bound;
initial_temperature = *initial_temperature + step_size)
{
auto converted_temperature = hest::Temperature::Convert(*initial_temperature, converted_unit);
std::cout << PrintResultRow(initial_temperature, converted_temperature) << std::endl;
}
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}发布于 2015-09-22 06:44:55
我不认为我会使用类层次结构来表示不同类型的温度。最后,它们代表着相同的事物(系统中的能量量)。您应该选择一个单元来存储数据,然后将所有类型隐藏到这个全局单元中。
class Temperature
{
double kelvin;
public:
// Explicit: We don't want auto-conversion.
explicit Temperature(double kelvin)
: kelvin(kelvin)
{}
};然后编写将特定比例转换为所选表单的make_temp_from_X()函数。
Temperature make_temp_from_kelvin(double k) {return Temperature(k);}
Temperature make_temp_from_celcius{double c) {return Temperature(c-273.15);}
... etc传递一个Temperature类型的对象的成本与传递一个double (所以您不需要担心这个问题)相同。
https://codereview.stackexchange.com/questions/105261
复制相似问题