Hello again!
In this video, we are going to look at unions.
Unions are inherited from the C programming language.
A union is a compound data structure.
It is similar to a struct in some ways, and different in some ways as well.
Each member of a union must have a distinct type.
We cannot have two members which have the same type.
All the members of a union are stored at the same address in memory, and only one member of a union
can be in use at any one time.
So what does this mean?
Let's try and visualize this.
If we imagine we have a struct whose members are character, int and double, the character will be stored
at the beginning of the struct memory.
The int member will follow the character, and the double will come after the int.
So they all come after each other in memory.
The compiler may also put in some so-called "padding" bytes.
These mean that the program
will access the character value as a word and not as a byte.
And that is much more efficient.
There may also be some padding bytes here as well, depending on the architecture.
If we have a union with the same members, then the character will again begin at the start of the union's
memory.
The int will also begin at the start of the union's memory, and its storage will overlap the character
storage. And the double
will also begin at the start of the union's memory and its storage will overlap the character and
the nt.
So you may think this is a trick for saving memory.
In fact, the main application of unions is if you are processing data, which could be one of
several types.
And that comes in useful if you are parsing soure code, for example, or processing data that you have
received over the network.
All the members of a union are public by default.
The same as a struct. In older versions of C++,
the members of a union must be simple data types: no constructor, copy constructor, assignment
operator or destructor.
So that means, for example, that you cannot have a string as a union member. Which is pretty limiting,
because strings come up all the time when you are processing data. In C++11,
this was relaxed a bit, but it is a bit messy, because the compiler will not generate calls to things
like destructors.
So you have to call them yourself. And I am not going to go into that here.
Unions can have member functions, but they cannot have virtual member functions. And you cannot use
a union as a base or a derived class.
To use a union,
we start by assigning to one of the members.
So we could assign to the character member for example, and that means that the character member is
now in use.
So the character member will have a well-defined value, and the int and the double will have undefined values.
The only safe thing we could do is to read from the character value, or we could assign to one of the other
members.
And then, that member will come into play and the character member will have an undefined value.
Let's have an example.
So we have a union.
The members are character, int and double.
We create an object of this union.
And then we assign to the character member.
So the character member is now in use. And it has a well-defined value, which is the letter, capital
'Z'.
If we try to access the double member, this will have an undefined value.
What will happen is that the program tries to interpret the member used by the union as a double.
The first byte of this memory will contain the ASCII code for capital 'Z' and the rest of the bytes will
contain - well, who knows?
So we will probably get some kind of garbage number.
Like that, for example.
So this is fairly typical of the C legacy.
It is low level.
It is easy to make bad mistakes, and it is difficult to use safely.
The main problem here is that the programmer is expected to know which type is in use.
If you are working on a small problem on your own, that is not really a problem.
If you have a large code base with several programmers working on it, then sooner or later there will be
a mistake.
One of the things you can do is to add a so-called "tag" member to the union.
So this is an extra member, which will just keep track of which member is in use.
And that is known as a tagged union.
So let's try that out.
We start off by creating an enumeration, with values for each possible allowed type.
So we have character, int and double.
Then we have an extra member, which is this token type.
If we are going to bring the character member into use, then we need to set the token type to character.
And then later on, if we want to use the double member, we can just check this.
And if the token type is double, then we know it is safe to use this.
And if it is not, then we have avoided something which is dangerous.
And there are, we get a much more sensible result this time.
But there is still a problem: we have to rely on the programmer who assigned to the character member.
We have to assume that they also remembered to set the token type.
We will come back to this in the next video. But
meanwhile, that is all for this one.
I will see you next time.
But until then, keep coding!