C++'s union type is untagged and therefore unsafe. Unions require the programmer to include a separate tag field indicating which field of the union is ``current.'' Unfortunately, in large projects it is increasingly likely that this tag is incorrect or is left out in a particular function either unintentionally or because ``that case can't possibly happen here.''
The following example shows a simple union and then the class hierarchy that replaces it. One advantage of the class hierarchy is that is provides automated tag checking. The particular union represents a literal pool entry for a compiler symbol table. A literal is assumed to be either an int, a char, or a float. Adding new literal kinds is discussed below. First the original union and an example function that operates on it.
enum literal_kind{INT, CHAR, FLOAT};
typedef struct
{
literal_kind kind;
union
{
int int_value;
char char_value;
float float_value;
};
} literal_union;
print_literal_union(literal_union *l)
{
switch(l->kind)
{
case INT: printf("%s = %d\n", l->name, l->int_value); break;
case CHAR: printf("%s = %c\n", l->name, l->char_value); break;
case FLOAT: printf("%s = %f\n", l->name, l->float_value); break;
}
}
The class hierarchy replacing this union includes the pure virtual class ``literal.'' Such a class can have no instances; rather, it provides an interface. In this case, all classes derived from literal must override all pure virtual functions (those whose declarations end with = 0). This gives different types of literals the same interface. (Constructors are not shown in the code.)
class literal
{
public:
virtual void print() = 0;
};
class int_literal : public literal
{
private:
int value;
public:
void print() {printf("%s = %d\n", name, value);};
};
class char_literal : public literal
{
private:
char value;
public:
void print() {printf("%s = %c\n", name, value);};
};
class float_literal : public literal
{
private:
float value;
public:
void print() {printf("%s = %f\n", name, value);};
};
The following code illustrate the violation possible with the union and
how the C++ class hierarchy avoids it.
[ 1] main()
[ 2] {
[ 3] literal_union lu;
[ 4]
[ 5] lu.kind = INT; // should be lu.kind = FLOAT;
[ 6] lu.float_value = 5.6;
[ 7] print_literal_union(&lu);
[ 8]
[ 9] int_literal il(5);
[10] il.print();
[11]
[12] int_literal fl(5.6); // compile time error
[13] fl.print();
[14] }
Notes