How to write a Java-enum-like class with multiple data fields in C ++?

Based on the Java background, I find C ++ enums very lame. I would like to know how to write Java-like enums (those in which enum values ​​are objects and can have attributes and methods) in C ++.

For example, translate the following Java code (enough of it to demonstrate the technique) to C ++:

public enum Planet { MERCURY (3.303e+23, 2.4397e6), VENUS (4.869e+24, 6.0518e6), EARTH (5.976e+24, 6.37814e6), MARS (6.421e+23, 3.3972e6), JUPITER (1.9e+27, 7.1492e7), SATURN (5.688e+26, 6.0268e7), URANUS (8.686e+25, 2.5559e7), NEPTUNE (1.024e+26, 2.4746e7); private final double mass; // in kilograms private final double radius; // in meters Planet(double mass, double radius) { this.mass = mass; this.radius = radius; } private double mass() { return mass; } private double radius() { return radius; } // universal gravitational constant (m3 kg-1 s-2) public static final double G = 6.67300E-11; double surfaceGravity() { return G * mass / (radius * radius); } double surfaceWeight(double otherMass) { return otherMass * surfaceGravity(); } public static void main(String[] args) { if (args.length != 1) { System.err.println("Usage: java Planet <earth_weight>"); System.exit(-1); } double earthWeight = Double.parseDouble(args[0]); double mass = earthWeight/EARTH.surfaceGravity(); for (Planet p : Planet.values()) System.out.printf("Your weight on %s is %f%n", p, p.surfaceWeight(mass)); } } 

Any help would be greatly appreciated!

Thank!

+45
java c ++ enums
Dec 27 '09 at 5:20
source share
4 answers

One way to model Java enumerations is to create a class with a private constructor that creates copies of itself as static variables:

 class Planet { public: // Enum value DECLARATIONS - they are defined later static const Planet MERCURY; static const Planet VENUS; // ... private: double mass; // in kilograms double radius; // in meters private: Planet(double mass, double radius) { this->mass = mass; this->radius = radius; } public: // Properties and methods go here }; // Enum value DEFINITIONS // The initialization occurs in the scope of the class, // so the private Planet constructor can be used. const Planet Planet::MERCURY = Planet(3.303e+23, 2.4397e6); const Planet Planet::VENUS = Planet(4.869e+24, 6.0518e6); // ... 

Then you can use the enums as follows:

 double gravityOnMercury = Planet::MERCURY.SurfaceGravity(); 
+63
Dec 27 '09 at 6:17
source share

With the introduction of C ++ 11 constexpr . There is another way to implement typed enumerations. One that works in much the same way as regular enums (stored as an int variable and can be used in a switch ), but also allows them to have member functions.

In the header file you must put:

 class Planet { int index; public: static constexpr int length() {return 8;} Planet() : index(0) {} constexpr explicit Planet(int index) : index(index) {} constexpr operator int() const { return index; } double mass() const; double radius() const; double surfaceGravity() const; }; constexpr Planet PLANET_MERCURY(0); constexpr Planet PLANET_VENUS(1); constexpr Planet PLANET_EARTH(2); // etc. 

And in the source file:

 static double G = 6.67300E-11; double Planet::mass() { switch(index) { case PLANET_MERCURY: return 3.303e+23; case PLANET_VENUS: return 4.869e+24; case PLANET_EARTH: return 5.976e+24; // Etc. } } double Planet::radius() { // Similar to mass. } double Planet::surfaceGravity() { return G * mass() / (radius() * radius()); } 

which can then be used as:

 double gravityOnMercury = PLANET_MERCURY.SurfaceGravity(); 

Unfortunately, enumeration records cannot be defined as static constants inside the class body. They must be initialized after the declaration, because they are constexpr , but inside the class the class is not yet a full type and therefore cannot be created.

+4
Apr 12 '15 at 21:42
source share

Maybe this is what you want -

 #include<iostream> using namespace std; class Planet { double mass,radius; Planet(double m, double r) : mass(m) : radius(r) {} public: static const Planet MERCURY; void show(){ cout<<mass<<","<<radius<<endl; } } ; const Planet Planet::MERCURY = Planet(1.0,1.2); int main(){ Planet p = Planet::MERCURY; p.show(); } 

This is just a little code, I'm sure you can change it to suit your needs.

+1
Dec 27 '09 at 6:38
source share

This is an ugly, verbose and usually stupid way. But I decided that I would send a complete code example as an explanation. For additional points, you can actually define an extended iteration of compilation time over solar planets by setting up template specializations only a little bit.

 #include <string> #include <sstream> #include <iostream> #include <cstdlib> class Planet { public: static const double G = 6.67300E-11; Planet(const ::std::string &name, double mass, double radius) : name_(name), mass_(mass), radius_(radius) {} const ::std::string &name() const { return name_; } double surfaceGravity() const { return G * mass_ / (radius_ * radius_); } double surfaceWeight(double otherMass) const { return otherMass * surfaceGravity(); } private: const ::std::string name_; const double mass_; const double radius_; }; enum SolarPlanets { MERCURY, VENUS, EARTH, MARS, JUPITER, SATURN, URANUS, NEPTUNE }; template <SolarPlanets planet> class SolarPlanet : public Planet { }; template <> class SolarPlanet<MERCURY> : public Planet { public: SolarPlanet() : Planet("MERCURY", 3.303e+23, 2.4397e6) {} }; template <> class SolarPlanet<VENUS> : public Planet { public: SolarPlanet() : Planet("VENUS", 4.869e+24, 6.0518e6) {} }; template <> class SolarPlanet<EARTH> : public Planet { public: SolarPlanet() : Planet("EARTH", 5.976e+24, 6.37814e6) {} }; template <> class SolarPlanet<MARS> : public Planet { public: SolarPlanet() : Planet("MARS", 6.421e+23, 3.3972e6) {} }; template <> class SolarPlanet<JUPITER> : public Planet { public: SolarPlanet() : Planet("JUPITER", 1.9e+27, 7.1492e7 ) {} }; template <> class SolarPlanet<SATURN> : public Planet { public: SolarPlanet() : Planet("SATURN", 5.688e+26, 6.0268e7) {} }; template <> class SolarPlanet<URANUS> : public Planet { public: SolarPlanet() : Planet("URANUS", 8.686e+25, 2.5559e7) {} }; template <> class SolarPlanet<NEPTUNE> : public Planet { public: SolarPlanet() : Planet("NEPTUNE", 1.024e+26, 2.4746e7) {} }; void printTerranWeightOnPlanet( ::std::ostream &os, double terran_mass, const Planet &p ) { const double mass = terran_mass / SolarPlanet<EARTH>().surfaceGravity(); os << "Your weight on " << p.name() << " is " << p.surfaceWeight(mass) << '\n'; } int main(int argc, const char *argv[]) { if (argc != 2) { ::std::cerr << "Usage: " << argv[0] << " <earth_weight>\n"; return 1; } const double earthweight = ::std::atof(argv[1]); printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<MERCURY>()); printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<VENUS>()); printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<EARTH>()); printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<MARS>()); printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<JUPITER>()); printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<SATURN>()); printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<URANUS>()); printTerranWeightOnPlanet(::std::cout, earthweight, SolarPlanet<NEPTUNE>()); return 0; } 
0
Dec 27 '09 at 5:24
source share



All Articles