Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Mail Systems
Eclipse Documentation

How To Guides
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Problem Solutions
Privacy Policy




Thinking in C++
Prev Contents / Index Next


As you’ve seen, the only difference between struct and class in C++ is that struct defaults to public and class defaults to private. A struct can also have constructors and destructors, as you might expect. But it turns out that a union can also have a constructor, destructor, member functions, and even access control. You can again see the use and benefit of overloading in the following example:

//: C07:UnionClass.cpp
// Unions with constructors and member functions
using namespace std;

union U {
private: // Access control too!
  int i;
  float f;
  U(int a);
  U(float b);
  int read_int();
  float read_float();

U::U(int a) { i = a; }

U::U(float b) { f = b;}

U::~U() { cout << "U::~U()\n"; }

int U::read_int() { return i; }

float U::read_float() { return f; }

int main() {
  U X(12), Y(1.9F);
  cout << X.read_int() << endl;
  cout << Y.read_float() << endl;
} ///:~

You might think from the code above that the only difference between a union and a class is the way the data is stored (that is, the int and float are overlaid on the same piece of storage). However, a union cannot be used as a base class during inheritance, which is quite limiting from an object-oriented design standpoint (you’ll learn about inheritance in Chapter 14).

Although the member functions civilize access to the union somewhat, there is still no way to prevent the client programmer from selecting the wrong element type once the union is initialized. In the example above, you could say X.read_float( ) even though it is inappropriate. However, a “safe” union can be encapsulated in a class. In the following example, notice how the enum clarifies the code, and how overloading comes in handy with the constructors:

//: C07:SuperVar.cpp
// A super-variable
#include <iostream>
using namespace std;

class SuperVar {
  enum {
  } vartype;  // Define one
  union {  // Anonymous union
    char c;
    int i;
    float f;
  SuperVar(char ch);
  SuperVar(int ii);
  SuperVar(float ff);
  void print();

SuperVar::SuperVar(char ch) {
  vartype = character;
  c = ch;

SuperVar::SuperVar(int ii) {
  vartype = integer;
  i = ii;

SuperVar::SuperVar(float ff) {
  vartype = floating_point;
  f = ff;

void SuperVar::print() {
  switch (vartype) {
    case character:
      cout << "character: " << c << endl;
    case integer:
      cout << "integer: " << i << endl;
    case floating_point:
      cout << "float: " << f << endl;

int main() {
  SuperVar A('c'), B(12), C(1.44F);
} ///:~

In the code above, the enum has no type name (it is an untagged enumeration). This is acceptable if you are going to immediately define instances of the enum, as is done here. There is no need to refer to the enum’s type name in the future, so the type name is optional.

The union has no type name and no variable name. This is called an anonymous union, and creates space for the union but doesn’t require accessing the union elements with a variable name and the dot operator. For instance, if your anonymous union is:

//: C07:AnonymousUnion.cpp
int main() {
  union { 
    int i; 
    float f; 
  // Access members without using qualifiers:
  i = 12;
  f = 1.22;
} ///:~

Note that you access members of an anonymous union just as if they were ordinary variables. The only difference is that both variables occupy the same space. If the anonymous union is at file scope (outside all functions and classes) then it must be declared static so it has internal linkage.

Although SuperVar is now safe, its usefulness is a bit dubious because the reason for using a union in the first place is to save space, and the addition of vartype takes up quite a bit of space relative to the data in the union, so the savings are effectively eliminated. There are a couple of alternatives to make this scheme workable. If the vartype controlled more than one union instance – if they were all the same type – then you’d only need one for the group and it wouldn’t take up more space. A more useful approach is to have #ifdefs around all the vartype code, which can then guarantee things are being used correctly during development and testing. For shipping code, the extra space and time overhead can be eliminated.

Thinking in C++
Prev Contents / Index Next

   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire