I’m teaching myself C++ out of a book, but have gotten hung up on enumerated constants. It starts by saying:
"Enumerated constants enable you to create new types and then to define variables of those types whose values are restricted to a set of possible values. (emphasis mine).
It goes on to say (using as an example an enumerated constant named color): “You can define variables of type color, but they can be assigned only one of the enumerated values…”. Fine, I think I get the idea. But THEN it says:
“In fact, you can assign any integer value, even if it is not a legal color, although a good compiler will issue a warning if you do”.
HUH? I thought the whole point was that the variable of the enumerated type could only have one of the pre-defined values. I tested the sample program in the book, and indeed the variable accepts any old short integer.
If it’s at all helpful, I’m referring to Teach Yourself C++ in 21 Days by Jesse Liberty pp. 44-45
C++, as originally designed, was built on top of C. C lets the programmer do really, really stupid things, if he asks to, even if he doesn’t realized he’s asking to. Sometimes, it will even generate a warning telling him what a blithering idiot he’s being. In any case, after the program is compiled, there is no implicit error checking (well, if you don’t count a core dump following a segmentation fault), allowing for faster programs.
When the compiler sees your enumeration construct, it maps the enumerant type onto the short integer type, and the different enumerants to different short values. When the program is running, an operation using one of the enumerants is actually using a short integer; the computer doesn’t inherently know the difference.
If you want to see something scary, try using a logical operator, such as xoring (red^blue) two enumerants together. On the other hand, this ability, plus the ability to declare each enumerant’s value allows for some quick operations if the programmer knows what he’s doing.
By using enumerants, it allows for a programmer to write clearer code, without using hard-to-maintain macros, and without a loss of speed.
I don’t know off the top of my head if enumeration constraints are part of ANSI c++(hell the last time I looked they hadn’t decided on an ANSI c++) but many compiliers have a switch that restricts you to formal construction(if not ANSI c++). If you have access to such a compiler then setting the switch will turn the warning into a compile error on the little things that are improper.
Basically, the C++ compiler maintains a list of strings (the colors) and maps them to a list of numbers (0, 1, 2, …). After the code is compiled, it is really using those numbers, not the colors any more.
So, if you put a valid number (not greater than the number of colors you supplied) in place of a color, the compiler thinks, “The programmer could be using shorthand because he/she knows what the number is. I’ll let them know this isn’t quite kosher, but, hey, a number is a number.”
This is why C++ isn’t a strongly typed language. But for a C programmer, it all seems natural.
What this ends up meaning is, unless your compiler warns you or gives errors, the only reason for using enumerated types is to make the code easier to read (by humans, that is). Of course, the same could be said of any type, even the ‘basic’ ones. I’ve known compilers that don’t even get upset at implicitly type casting (i.e. sending a function that expects characters a byte-size integer instead of a character).
(flame-bait) It’s still much better than any B&D language.
And if you really want to understand the C underlying C++ (and Java, mostly) get Kernighan & Ritchie (the thin white book with blue writing on the cover).
Yes, but it’s more than that. The symbolic constants have a relationship to each other. In the colors example, “blue” > “green” or whatever. You can increment them (in some implementations, probably not ANSI), add them (red + blue = green, if you pick the order right) etc.
The key thing is that you can’t depend on the compiler to do what you expect, since different compilers do different things with the same input. Playing games like this is very implementation dependent, and you will get burned if you use it often enough.
Well, I dug this out of my Borland C++ help files, from the reference on the enum command:
“In C, an enumerated variable can be assigned any value of type int–no type checking beyond that is enforced. In C++, an enumerated variable can be assigned only one of its enumerators.”
So I guess my compiler is accepting the C standard to maintain backward compatability. Now, if anyone can explain one other related question. In the same sample program, a enumerated constant type called Days and a variable of type Days called DayOff has been created. Then an int variable x has been defined and used to get a single short int from the keyboard. The program then says:
DayOff = Days(x);
I don’t understand the syntax of this line. It does NOT mean “Have DayOff equal the value of the Xth constant of Days”. I tested that by initializing the constants, so that for example the first constant was Sunday=400, I typed 1 for x, and DayOff ended up equal to 1. In fact, I don’t understand why the line wasn’t just written “DayOff = x;”
Heres my “I left all my C++ books at work” answer:
Typecasting in C++ is just the application of a unary operator of the form <typename> <expression>. (<typename> being the operator, c.f. the way you overload a typecast)The parenthesies around the typename are optional, but almost always used.
[aside]
If you haven’t run into it before, typecasting is a way of telling the compiler that you know more about datatypes than it does. For example, lets say you’re writing a callback function that gets called every time some button on the screen gets clicked. You’re going to want to modify some of your internal data structures in response to the click, so you need to have them passed into the callback function. Unfortunately, the person who wrote the button library had no idea what datatypes you were going to use, but you can use a void* pointer (your most basic generic pointer). Well, then you have to take this generic pointer and tell the compiler what it “really” is, and you do this using a typecast, ususally of the form (MyDataType*)generic_pointer;
[subaside]
C++ now has 4 typecasting “operators” to replace C’s 1. There is const_cast, dynamic_cast, reinterpret_cast, and static_cast. If your compiler uses these, you should switch over to them as soon as possible. They make code maintenance much easier (its WAY easier to tell what a line of code is doing if you have a big ugly reinterpret_cast<type>(expr) there that if you just had (type)expr)
[/subaside]
[/aside]
So I would guess that
DayOff = Days(x);
is the same as
DayOff = (Days)(x);
which is the same as
DayOff = (Days)x;
which is the standard way of casting.
or in New Improved ANSI Standard C++ ™:
DayOff = static_cast<Days>(x);
And I think the only reason they are writing it in the first way is that it looks like a constructor call. Which it actually would be in any language that made enumerated types a real type and not just a long way of writing ints.
Most of the previous is advanced beyond what I’ve learned yet, but I gather that it amounts to “They do it that way because it follows a formal standard, even if it’s not strictly necessary in this instance”. I just needed reassurance that I wasn’t misunderstanding the whole thing. Now I can keep going in my lessons.