I learned enums when I learned C and from time to time I keep myself reminding about it and most of the time by re-reading from some source,it occurred to me that this is due to the fact I never use it in my programming,my programming interest concentrate around algorithmic problem solving so I am not sure where could I possibly use enums.
开发者_如何学PythonCould somebody suggest some good example where using enums is makes things much easy?
I would appreciate algorithmic examples but both of algorithmic or non-algorithmic examples are welcome.
Imagine that you are programming a depth first search and you want to tag your edges with whether they are tree, back, forward, or cross. You could create an enum EDGE_TYPE with the four possibilities and use it to tag your edges.
When describing/observing some attribute of some system you might find that attribute can have any value from a limited set. Name those values, assign each an integer value (code), collect them in an enumeration and you have defined a type of that attribute. All actions considering that attribute can now use this type.
Example: for some system we can consider its state as one of its attributes. We can observe it and say that it can be in 'uninitialized' state, state of 'initialization', 'active' or 'idle' state (feel free to add more states here...). If you want to perform some action on that system but which depends on the current state, how will you pass state information to that action (function)? You can pass strings 'uninitialized', 'initialization'...but more efficient, simple and safe from errors would be if you would pass just one integer from a set:
enum State
{
Uninitialized,
Initialization,
Active,
Idle
};
That function will have State as an argument and can use switch when deciding what to do depending on the current state:
void foo(..., const State state,...)
{
...
switch(state)
{
case Uninitialized:
cout << "Uninitialized" << endl;
break;
case Initialization:
...
}
...
}
Using enumeration type to describe a limited set of attribute's values is safer then using a set of #defines and integer variable. E.g. if you have:
#define UNINITIALIZED 0
#define INITIALIZATION 1
#define ACTIVE 2
#define IDLE 3
and
int nState;
nothing can stop you to assign any integer value to nState:
nState = 4; // What state is 4?
If you use enumeration:
State state;
You cannot assign to it an arbitrary integer value but only an enumerator (although underlying type for enumeration is integer! - see this):
state = Active;
I use them as parameters to functions as opposed to using booleans to improve readability of my code.
Take a look at the rationale about enum at Wikipedia.
Worth mentioning too: Enumerated Types - enums
One use of enums is to make code clearer at the call site. Compare:
//Usage: Kick(Dog);
enum PetType
{
Cat,
Dog
};
void Kick(PetType p)
{
switch(p)
{
case Cat:
//Kick Cat
break;
case Dog:
//Kick Dog
break;
default:
//Throw an exception.
break;
}
}
//Usage: Kick(false);
void Kick(bool isCat)
{
if (isCat)
{
//Kick Cat
}
else
{
//Kick Dog
}
}
Even though a boolean would work just as well, someone unfamiliar with the function will need to work much harder to determine what it does in the case that a boolean was used. Kick(Dog)
is much clearer than Kick(false)
.
with all said about using enums as symbolic constant, I'd like to emphasize that in C++ using enum in a class gives a very nice, readable and convenient way of encapsulating and exposing class's capabilities, e.g.
class BlockCipher {
public:
enum PaddingOptions { NO_PADDING, DEFAULT_PADDING, ZERO_PADDING /*...*/ }
void encrypt(const std::string& cleartext, std::string& ciphertext,
PaddingOptions pad=DEFAULT_PADDING);
};
int main()
{
std::string clear("hello, world");
std::string encrypted;
BlockCipher encryptor;
encryptor.encrypt(clear, encrypted, BlockCipher::NO_PADDING);
}
enum
s can make code easier to read and may present better type checking during compilation.
Issues With Enums
- They can be converted to
int
orunsigned int
and assigned into those kind of variables, thus creating a hole in the type checking benefit. - Their symbol name cannot be printed
directly. Passing an
enum
tostd::cout
results in theenum
converted to an integer then printed out. Most implementations must perform a table lookup to convert theenum
to text before printing.
Alternatives
Another alternative to enum
is to use a string. I've worked at shops where they pass a constant string instead of an enum. One advantage is that the named value is always available, even when debug symbols are not. Also there are no conversions required when printing.
Some disadvantages to strings:
- Can't be used in
switch
statement. - Case sensitivity when comparing.
- Comparing may take more execution time.
- Occupies more data or executable space.
You can have first value and last value and everything else inbetween those values. The every where in the code check if your values are in the range. NOW add new values to your enum between first and last and don't wory abouth changing all of these checks!
typedef enum
{
First_value,
Uninitialized,
Initialization,
Active,
Idle,
Last_value
} my_type;
void function(my_type state)
{
if ((state > First_value) && (state < Last_value))
{
//Do stuff...
}
}
Enums have one advantage over #define
, but it's purely an implementation detail: debuggers typically can show/use enum
values but #define
d values.
On the other hand, #define
has several fundamental advantages, one of which is that you can test for the existence with #ifdef
. This is useful if you need to support multiple versions of a library, and want to optionally use new enum-like choices if they're available.
Some library authors use a hybrid approach of first defining the constants with enum
then:
#define FOO FOO
#define BAR BAR
etc.
I started a personal project in, and I wanted to identify my packet ID, it looks like this:
enum{
//Client to server
//Connection
ID_KEEP_ALIVE = 0x00,
ID_LOGIN_REQUEST = 0x01,
ID_CONNECTING = 0x02,
ID_DISCONNECT = 0x03,
//Player actions
ID_PLAYER_INFO = 0x04,
ID_PLAYER_MOVE = 0x05,
ID_PLAYER_ATTACK = 0x06,
//Inventory
ID_LOOT_ITEM = 0x10,
ID_DESTROY_ITEM = 0x12,
ID_USE_ITEM = 0x13,
ID_EQUIP_ITEM = 0x15,
ID_UNEQUIP_ITEM = 0x16,
ID_DROP_ITEM = 0x17,
};
and then, when I receive a packet, I've got a huge switch that looks like this to process the packets and send them:
switch(packet.packetID){
case ID_KEEP_ALIVE:
//...
break;
case ID_LOGIN_REQUEST:
//...
break;
case ID_CONNECTING:
//...
break;
case ID_DISCONNECT:
//...
break;
//..
}
it's my best example, enjoy :)
精彩评论