We have discussed the fundamental data type in the previous section. This data type is used in defining or initializing a variable to ensure that the variable can store the selected data type. However, there are other data types that can be used to define a variable. They are enum (enumeration) and struct.
Enumeration is a data type that has several possible values and they are defined as the constant which is called enumerators. It is used to create a collection of constants. Suppose we want to develop a card game using C++. As we know, a deck of playing cards contains 52 cards, which consists of four suits (Clubs, Diamonds, Hearts, and Spades) with 13 elements in each suit. We can notate the card deck as follows:
enum CardSuits
{
Club,
Diamond,
Heart,
Spade
};
enum CardElements
{
Ace,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
Jack,
Queen,
King
};
If we want to apply the preceding enum data types (CardSuits and CardElements), we can use the following variable initialization:
CardSuits suit = Club;
CardElements element = Ace;
Actually, enums always contain integer constants. The string we put in the enum element is the constant name only. The first element holds a value of 0, except we define another value explicitly. The next elements are in an incremental number from the first element. So, for our preceding CardSuits enum, Club is equal to 0, and the Diamond, Heart, and Spade are 1, 2, and 3, respectively.
Now, let's create a program that will generate a random card. We can borrow the GenerateRandomNumber() function from our previous code. The following is the complete code for this purpose:
// Enum.cbp
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
enum CardSuits
{
Club,
Diamond,
Heart,
Spade
};
enum CardElements
{
Ace,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine,
Ten,
Jack,
Queen,
King
};
string GetSuitString(CardSuits suit)
{
string s;
switch(suit)
{
case Club:
s = "Club";
break;
case Diamond:
s = "Diamond";
break;
case Heart:
s = "Heart";
break;
case Spade:
s = "Spade";
break;
}
return s;
}
string GetElementString(CardElements element)
{
string e;
switch(element)
{
case Ace:
e = "Ace";
break;
case Two:
e = "Two";
break;
case Three:
e = "Three";
break;
case Four:
e = "Four";
break;
case Five:
e = "Five";
break;
case Six:
e = "Six";
break;
case Seven:
e = "Seven";
break;
case Eight:
e = "Eight";
break;
case Nine:
e = "Nine";
break;
case Ten:
e = "Ten";
break;
case Jack:
e = "Jack";
break;
case Queen:
e = "Queen";
break;
case King:
e = "King";
break;
}
return e;
}
int GenerateRandomNumber(int min, int max)
{
// static used for efficiency,
// so we only calculate this value once
static const double fraction =
1.0 / (static_cast<double>(RAND_MAX) + 1.0);
// evenly distribute the random number
// across our range
return min + static_cast<int>(
(max - min + 1) * (rand() * fraction));
}
int main()
{
// set initial seed value to system clock
srand(static_cast<unsigned int>(time(0)));
// generate random suit and element card
int iSuit = GenerateRandomNumber(0, 3);
int iElement = GenerateRandomNumber(0, 12);
CardSuits suit = static_cast<CardSuits>(
iSuit);
CardElements element = static_cast<CardElements>(
iElement);
cout << "Your card is ";
cout << GetElementString(element);
cout << " of " << GetSuitString(suit) << endl;
return 0;
}
From the preceding code, we can see that we can access the enum data by using an integer value. However, we have to cast the int value so that it can fit the enum data by using static_cast<>, which is shown as follows:
int iSuit = GenerateRandomNumber(0, 3);
int iElement = GenerateRandomNumber(0, 12);
CardSuits suit = static_cast<CardSuits>(iSuit);
CardElements element = static_cast<CardElements>(iElement);
If we build and run the code, we will get the following console output:
Another advanced data type we have in C++ is structs. It is an aggregate data type which groups multiple individual variables together. From the preceding code, we have the suit and element variables that can be grouped as follows:
struct Cards
{
CardSuits suit;
CardElements element;
};
If we add the preceding struct to our preceding Enum.cbp code, we just need to refactor the main() function as follows:
int main()
{
// set initial seed value to system clock
srand(static_cast<unsigned int>(time(0)));
Cards card;
card.suit = static_cast<CardSuits>(
GenerateRandomNumber(0, 3));
card.element = static_cast<CardElements>(
GenerateRandomNumber(0, 12));
cout << "Your card is ";
cout << GetElementString(card.element);
cout << " of " << GetSuitString(card.suit) << endl;
return 0;
}
If we run the preceding code (you can find the code as Struct.cbp in the repository), we will get the same output as Enum.cbp.