开发者

C++ - best way to do an array of different implementations

开发者 https://www.devze.com 2023-03-22 18:25 出处:网络
I am trying to practice OO design. I\'ve made the following classes, which will eventually let a user pick a temperature unit type (like fahrenheit) from a list, and convert celcius temperatures to th

I am trying to practice OO design. I've made the following classes, which will eventually let a user pick a temperature unit type (like fahrenheit) from a list, and convert celcius temperatures to their chosen temperature:

TemperatureUnits.h:

#include "fahrenheittemperatureunit.h"
#include "kelvintemperatureunit.h"
#include "temperatureunit.h"

#include <list>

class TemperatureUnits
{
public:
    static std::list<TemperatureUnit> All();
    const static FahrenheitTemperatureUnit kFahrenheit;
    const static KelvinTemperatureUnit kKelvin;
};

TemperatureUnits.cpp:

#include "temperatureunits.h"

std::list<TemperatureUnit> TemperatureUnits::All()
{
    std::list<TemperatureUnit> units;
    units.push_back(TemperatureUnits::kFahrenheit);
    units.push_back(TemperatureUnits::kKelvin);
    return units;
}

const KelvinTemperatureUnit TemperatureUnits::kKelvin;
const FahrenheitTemperatureUnit TemperatureUnits::kFahrenheit;

TemperatureUnit.h

class TemperatureUnit
{
public:
    explicit TemperatureUnit(const std::string &name);
    virtual ~TemperatureUnit() {}

    const std::string& Name() const;
    virtual float FromCelcius(float celcius) const {return 0;}

private:
    std::string n开发者_JAVA百科ame_;
};

TemperatureUnit.cpp:

#include "temperatureunit.h"

TemperatureUnit::TemperatureUnit(const std::string &name)
    :
    name_(name)
{

}

const std::string& TemperatureUnit::Name() const
{
    return name_;
}

Lastly, an example subclass:

KelvinTemperatureUnit.h:

#include "temperatureunit.h"

class KelvinTemperatureUnit : public TemperatureUnit
{
public:
    explicit KelvinTemperatureUnit();
    virtual float FromCelcius(float celcius) const;
};

KelvinTemperatureUnit.cpp:

#include "kelvintemperatureunit.h"

KelvinTemperatureUnit::KelvinTemperatureUnit()
    :
    TemperatureUnit(std::string("Kelvin"))

{

}

float KelvinTemperatureUnit::FromCelcius(float celcius) const
{
    return celcius - 273;
}

There are a number of flaws (that I know of) in the above:

  • I'm not sure the class TemperatureUnits{ ... static std::list<TemperatureUnit> All(); pattern is the correct one to use, but unsure what would actually be better.

  • By storing the types of TemperatureUnit in a std::list, they are treated as the base class TemperatureUnit, instead of their specialisations like KelvinTemperatureUnit. This means calling the FromCelcius method doesn't work correctly. I am pretty sure I used to do this type of thing using Java.

  • I had a go, but it is probably pretty ugly and will offend someone :(

Does anyone have any ideas on the best way to go about this?

thanks!


The reason what you do works in Java is because all invocations are virtual or static in Java and objects are passed around using pointers rather than copies. which is not the case in C++.

This is because the vtable is not copied to the base class on you All() method. Thus you need to store rvalue references which requires C++0x compiler, or just pass pointers of the base class. Changes required are as follows:

static std::list<TemperatureUnit*> All():

The implementation would be:

std::list<TemperatureUnit*> TemperatureUnits::All()
{
    std::list<TemperatureUnit*> units;
    units.push_back(&TemperatureUnits::kFahrenheit);
    units.push_back(&TemperatureUnits::kKelvin);
    return units;
}


I am pretty sure I used to do this type of thing using Java.

Java objects are references. C++ objects are values. Simply convert to a reference, and then you will have more equivalent code. Oh, and a Java list is a C++ vector. A C++ list is a linked list.

Quite simply, do not bother to carry over any Java knowledge to C++, they are extremely different languages, you should start with C++ from scratch. C++ worth writing looks extremely different to Java.

int main() {
    std::vector<TemperatureUnit*> units;
    FahrenheitTemperatureUnit kFahrenheit;
    KelvinTemperatureUnit kKelvin;
    units.push_back(&kFahrenheit);
    units.push_back(&kKelvin);

    // Do something here
}


Your container of TemperatureUnit should be a container of TemperatureUnit*. In the former case, you have a container of unspecialised instances and the latter is a container of interfaces.

Difference between Java and C++


I'm not sure what the goal is here but why not just 1 Temperature class along the lines of?

class Temperature {

public:

  enum class TemperatureUnit {
    KELVIN,
    CELCIUS
  }

  Temperature( double value, TemperatureUnit unit );
  void set_value( double value, TemperatureUnit unit );
  double value( TemperatureUnit unit ) const;

private:

  double value_; // Always stored internally as KELVIN
};
0

精彩评论

暂无评论...
验证码 换一张
取 消