Section 4 - Enums
1A.4.1 Defining an enum Type
Enums are very simple. If we have a small, finite number of values that we want to use in our programs, and these values have intuitive meanings, we can create a separate data type for them. A good example is the days of the week:
enum Day {monday, tuesday, wednesday, thursday, friday, saturday, sunday};
The above can be placed outside the main() method (i.e., at global scope), inside a method or in a class prototype. Wherever it appears, it defines a new data type called Day. Day's only legal values are the identifiers listed in the braces, monday, tuesday, etc. In other words, if we define a variable of type Day, we can only assign it one of those seven values. We will see this in a moment.
Another example of this kind of declaration is as follows:
enum Color {red, orange, yellow, green, blue, indigo, violet};
which defines the Color data type. Or:
enum Planet {mercury, venus, earth, mars, jupiter, saturn, uranus, neptune};
Just think. Astronomy students three hundred years from now will be tested on the century in which Pluto lost its stature as a planet.
enum Planet {MERCURY, VENUS, EARTH, MARS, JUPITER, SATURN, URANUS, NEPTUNE}; enum Day {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};I also use UPPER_CASE for enum constants at times, but not always, and today I'm using lower_case. There is enough parity among programmers that I don't insist on one version or the other. However, our previous convention of using UPPER_CASE for const members still holds, and I do want you to keep doing that.
1A.4.2 Using an enum Type
Once the enum has been declared, you can create variables and use its values just as you would any other C++ data type:
Day appointment; Color eyes; Planet destination; appointment = monday; eyes = blue; destination = mars;
Don't be confused though; the words monday and blue become literals or constants that cannot be used like variables. Once we declare them to be part of an enum list, they cannot be used as anything else inside their scope (the method or class in which they appear). Nor are they strings. The word monday is a primitive Day just as 34.125 is a primitive double.
1A.4.3 Stringizing enum Types
Enums can't be printed as you might expect. If you did this:
cout << eyes; // contains the value blue cout << mars;
you would not get "blue" and "mars" on the screen, but actually the int 4 in the first case and 3 in the second. Why 4 and 3? Because the enum literals are really stored as ints starting with 0. In the case of Color above, red = 0, orange = 1, ... , blue = 4 because we start with 0 by default and increment by 1 for each enum literal in the list. Similarly with the Planet literals. mercury = 0, ... mars = 3.
So how do we convert these enums to strings that can be used? It's a little awkward. You have to supply the logic. For example:
if (destination == mercury) cout << "Mercury"; else if (destination == venus) cout << "Venus"; // etc.
This is long winded but it is usually how it is done. We often provide a so-called stringizing function to convert from the enum to the string we want to appear on the screen.
1A.4.4 Enum ordering
Because we declare enum types as a list, there is an inherent ordering (less than, <, greater than, >) for the values. By default, the ints associated with the enum literals are used to determine the ordering. So we can ask:
if (destination > jupiter)
Since we assigned mars to the Planet variable destination, and because mars comes before jupiter in the Planet list we defined, this if statement would "test out" as false; mars is not greater than jupiter.
1A.4.5 Suits
In our Card data type, we'll need to define an enum type called Suit as follows:
enum Suit { clubs, diamonds, hearts, spades };
Once done, we can use Suit as if it were a data type, and we can use the four Suits as literals without quotes inside the program as if they were literal constants (i.e., not as strings and without quotes). That is what we shall do in a few moments.
Meanwhile, a safe bet is to try to use enum types as if they were ints. For example, you can use an enum in a switch statement, or compare two enums (more about that in a moment).
1A.4.5.1 At Global Scope
When defined outside a class, the enum type can be used anywhere by merely stating its name. Here's an example:
// global scope definition enum Suit { clubs, diamonds, hearts, spades }; // class Card prototype ----------------- class Card { }; // client or other classes -------------- int main() { Card card3('9', hearts); } // class method definitions -------------- Suit Card::getSuit() { return suit; }
1A.4.5.2 As an Inner Type
When the enum is defined within the scope of the class prototype, it needs to be in the public section (if you are allowing and expecting the clients to be able to use this type). Also, when anyone outside the class needs this type, it has to dereference the class name using the scoping operator as shown:
// class Card prototype ----------------------- class Card { public: // internal definition enum Suit { clubs, diamonds, hearts, spades }; }; // client or other classes -------------- int main() { Card card3('9', Card::hearts); } // class method definitions -------------- Card::Suit Card::getSuit() { return suit; }
Of course, we typically define our one-line accessors in-line so the method definition would not be external to the class prototype. If that were the case, then the return type of the getSuit() method would not require the Card:: prefix.
1A.4. 6 Looping Over Enums
You can't treat enums exactly like ints. For example using enums in loops is often a desirable shortcut to process some date, but it is not always possible.
The first idea that you might think of won' t even compile:
for (day = monday; day <= sunday; day++) cout << day << endl;
The problem is not the cout statement or the first two parts of the for control; they are both legal (the enum is treated like an int in those statements, and all is well). The problem is the day++; enums can't be incremented, since there is no guarantee that Days are sequential (you can manually assign any integer to any enum (look that one up on your own).
But if you use the default ordering, i.e., let the first enum constant correspond to 0, and each subsequent constant be one larger than the constant to its left (the default), then you can cheat to get an enum loop to work: use an int for the loop counter, not an enum:
for (int k = monday; k <= sunday; k++) { // day = (Day)k; // either this old-style cast (fine) day = static_cast<Day>(k); // or the more accepted cast cout << day << endl; }
This question is everywhere on the web because new C++ learners always want to loop over enums. But remember: this only works if you don't manually override the int association, which provides monday = 0, tuesday = 1, ... .