Recall the classification of types given earlier:
Primitive Types
bool
char, wchar_t
signed char, unsigned char
short, int, long
unsigned short, unsigned int
(unsigned), unsigned long
signed short, signed int, signed long
float, double, long double
void
Pointer Types
Reference Types
Array Types
Programmer-Defined Types
enumerations, classes, structs, and
unions
We have not yet discussed the programmer-defined types. We have already seen that programmers can introduce new names for existing types using the typedef declaration:
typedef double REAL;
This only makes REAL a synonym for double, it doesn't introduce a brand new type.
An enumeration is a user-defined type consisting of a finite number of names. For example:
enum Suit {CLUB, DIAMOND, HEART, SPADE};
enum Value
{
ACE, TWO, THREE, FOUR, FIVE,
SIX, SEVEN, EIGHT, NINE,
TEN, JACK, QUEEN, KING
};
The names in an enumeration can be used like integer constants. The definition of Suit is equivalent to:
enum Suit {CLUB = 0, DIAMOND = 1, HEART = 2, SPADE = 3};
Of course programmers can initialize the names in an enumeration in different ways.
Sometimes values in one type can be converted to equivalent values in another type. Sometimes C++ will automatically perform these conversions where needed, other times programmers must explicitly request the conversion using a casting operator.
C++ will automatically perform conversions between its built-in types:
int x = 63;
double y = 42.5;
x = y; // x = 42
y = x; // y = 63.0
But some conversions involving user-defined types must be explicit. For example, C++ will automatically convert members of Suit to integers, but the reverse conversion must be explicit:
Suit u = 3; // error
int y = SPADE; // ok
Suit x = Suit(3); // ok, x == SPADE
Suit z = (Suit)3; // ok, but deprecated syntax
Suit w = Suit (25); // undefined, 25 out of range
A structure (i.e., a struct) is a convenient way for grouping variables (later we will learn structures can do a lot more):
struct Card
{
Value value;
Suit suit;
bool dealt; // = true if played
};
Declaring a structure:
Card c1 = {ACE, HEART, false}, c2 = {KING, SPADE, false}, c3;
declares the variables it encapsulates:
c1.value (= ACE)
c1.suit (= HEART)
c1.dealt (= false)
C++ allows assignment between structures:
c3 = c1;
c1.suit = SPADE;
cout << c3.suit; // prints 3 (= HEART)
Structures can be passed as parameters and returned as return values.
Here's a feeble way for printing cards. Write a better one later:
ostream& write(const Card& card, ostream& os = cout)
{
os << "[Suit = "
<< card.suit << ", ";
os << "Value = " <<
card.value << "]\n";
return os;
}
A union is similar to a struct, except that only one of the fields may contain a value at any time. Without type tags to tell us which field contains a value, unions are almost useless.
Example:
enum Type { REAL, INT };
union RealOrInt
{
int intVal;
double realVal;
};
struct Number
{
Type tag;
RealOrInt val;
};
// number constructors:
Number makeNumber(double v)
{
Number result;
result.tag = REAL;
result.val.realVal = v;
return result;
}
Number makeNumber(int v)
{
Number result;
result.tag = INT;
result.val.intVal = v;
return result;
}
void print(Number n)
{
if (n.tag == REAL)
{
cout << "Real: ";
cout << n.val.realVal <<
endl;
}
else if (n.tag == INT)
{
cout << "Integer: ";
cout << n.val.intVal <<
endl;
}
else cout <<
"Unknown\n";
}
int main()
{
Number n1 = makeNumber(3.2);
Number n2 = makeNumber(4);
print(n1);
print(n2);
return 0;
}
Here's the program output:
Real: 3.2
Integer: 4