Structures, Unions, and Enumerations

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.

Enumerations

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.

Type Conversion

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

Structures[1]

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;
}

Unions

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

 



[1] Class is identical to struct except that the default scope of the fields is private.