2009-12-27 2 views
46

Da ich aus einem Java-Hintergrund komme, finde ich C++ Enum sehr lahm. Ich wollte wissen, wie man Java-ähnliche enums schreibt (diejenigen, in denen die enum-Werte Objekte sind und Attribute und Methoden haben können) in C++.Wie schreibe ich eine Java-enum-ähnliche Klasse mit mehreren Datenfeldern in C++?

Übersetzen Sie zum Beispiel den folgenden Java-Code (einen Teil davon, ausreichend, um die Technik zu demonstrieren) zu 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)); 
    } 
} 

Jede Hilfe geschätzt sehr würde!

Danke!

+0

Da dies scheint eine beliebte Frage zu sein, ziehe zitieren das Original: http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html – trashgod

+0

Ich hatte es hier zitiert in der Bemerkungen. Ich habe keine Ahnung wer die Kommentare hier gelöscht hat. – missingfaktor

+0

Ich werde meine Antwort erweitern. Ich denke, die Antwort, die Sie bekommen haben, war wahrscheinlich besser. Aber ich denke, dass meine Antwort immer noch eine interessante Technik veranschaulicht. – Omnifarious

Antwort

65

Eine Möglichkeit, Java Aufzählungen zu simulieren ist, eine Klasse mit einem privaten Konstruktor zu erstellen, die Kopien von sich selbst als statische Variablen instanziiert:

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); 
// ... 

Dann können Sie die Aufzählungen wie folgt verwenden:

double gravityOnMercury = Planet::MERCURY.SurfaceGravity(); 
+1

Nun, danke! Es funktioniert jetzt! :) – missingfaktor

+3

das ist auch so ziemlich der Code, den Java unter der Haube generiert. – TofuBeer

+3

Leider fehlen noch einige der Java-Funktionen wie Iteration über Enum-Werte automatische name/toString-Funktion ... aber es ist immer noch eine nette Implementierung. –

1

Dies ist hässlich, ausführlich und in der Regel eine dumme Art zu gehen. Aber ich dachte, ich würde ein vollständiges Codebeispiel zur Erklärung veröffentlichen. Für zusätzliche Punkte ist es sogar möglich, eine kompilierte Zeitverlängerung über die Sonnenplaneten zu definieren, indem man die Template-Spezialisierungen nur ein wenig verfeinert.

#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

Planet und Planet wird als zwei verschiedene Arten identifiziert werden, richtig? Ich möchte in der Lage sein, sie mit der gleichen Art von Referenz zu verweisen, wie ich es in Java tun kann. (z. B. Planet currentPlanet = Planet.EARTH und Planet nextPlanet = Planet.MARS) – missingfaktor

+0

...und die Template-Spezialisierung wird mir verschiedene Typen und keine eindeutigen Werte geben! – missingfaktor

+0

Ich hatte es eilig, aber deine Einwände sind irgendwie korrekt, aber irgendwie nicht, also werde ich es dir zeigen. – Omnifarious

1

Kann sein, das ist, was Sie wollen -

#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(); 
} 

Dies ist nur ein kleiner Code, ich bin sicher, Sie dies ändern können an Ihre Bedürfnisse anpassen ..

5

Mit C++ 11 die Einführung von constexpr. Es gibt noch eine weitere Möglichkeit, typisierte Enums zu implementieren. Eine, die praktisch genauso funktioniert wie normale Enums (wird als int Variable gespeichert und kann in einer switch-Anweisung verwendet werden), erlaubt ihnen aber auch Memberfunktionen.

In der Header-Datei würden Sie setzen:

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. 

Und in der Quelldatei:

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()); 
} 

die dann eingesetzt werden als:

double gravityOnMercury = PLANET_MERCURY.SurfaceGravity(); 

Leider Enum Einträge können nicht als statische Konstanten innerhalb des Klassenkörpers definiert werden. Sie müssen bei der Deklaration initialisiert werden, da sie constexpr sind, aber innerhalb der Klasse ist die Klasse noch kein vollständiger Typ und kann daher nicht instanziiert werden.

+0

Ich mag das! Gibt es eine Möglichkeit, die harte Codierung von Indizes und deren Länge zu vermeiden? Ich suche nach enum "Klasse" in meiner Suche nach einer Lösung für dieses Problem: http://stackoverflow.com/questions/39687392/use-of-enums-for-indexing-arrays-in-a-relay-table – ShS

+0

Wahrscheinlich können Sie globale Variablen verwenden und diese beim Programmstart initialisieren. Obwohl dies eine Menge unnötiger Komplexität hinzufügt. Wenn die Enum-Einträge zur Kompilierzeit nicht bekannt sind, sollten Sie keine Enumeration verwenden. Eine Nachschlagetabelle oder std :: map wäre in diesem Fall besser geeignet. – bcmpinc

Verwandte Themen