Using C++ as better CUSENIX

  mccluskey, glen

by Glen McCluskey
<glenm@glenmccl.com>

Glen McCluskey is a consultant with 15 years of experience and has focused on programming languages since 1988. He specializes in Java and C++ performance, testing, and technical documentation areas.

In this column, we'll spend some time talking about C++ classes, structs, and unions, illustrating several points unrelated to object-oriented programming.

Type Names

In C, a common style of usage is to say:

struct A {
  int x;
};
typedef struct A A;

after which A can be used as a type name to declare objects:

void f()
{
  A a;
}

In C++, classes, structs, unions, and enum names are automatically type names, so you can say:

struct A {
   int x;
};

void f()
{
  A a;
}

or:

enum E {ee};
void f()
{
  E e;
}

By using the typedef trick, you can follow a style of programming in C somewhat like that used in C++.

But there is a quirk or two when using C++. Consider usage like:

struct A {
  int x;
};

int A;
void f()
{
  A a;
}

This is illegal because the int declaration A hides the struct declaration. The struct A can still be used, however, by specifying it via an "elaborated type specifier":

struct A

The same applies to other type names:

class A a;
union U u;
enum E e;

Taking advantage of this feature, that is, giving a class type and a variable or function the same name, isn't very good usage. It's supported for compatibility reasons with old C code; C puts structure tags (names) into a separate namespace, but C++ does not. Terms like "struct compatibility hack" and "1.5 namespace rule" are sometimes used to describe this feature.

Bit Field Types

Here's a small difference between C and C++. In ANSI C, bit fields must be of type "int," "signed int," or "unsigned int." In C++, they may be of any integral type, for example:

enum E {e1, e2, e3};

class A {
public:
  int x : 5;
  unsigned char y : 8;
  E z : 5;
};

This extension was added in order to allow bit field values to be passed to functions expecting a particular type, for example:

void f(E e)
{
}

void g()
{
  A a;
  a.z = e3;
  f(a.z);
}

Note that even with this relaxation of C rules, bit fields can be problematic to use. There are no pointers or references to bit fields in C++, and the layout and size of fields is tricky and not necessarily portable.

Anonymous Unions

Here's a simple one. In C++, this usage is legal:

struct A {
  union {
    int x;
    double y;
    char* z;
  };
};

whereas in C, you'd have to say:

struct A {
  union {
    int x;
    double y;
    char* z;
  } u;
 };

giving the union a name. With the C++ approach, you can treat the union members as though they were members of the enclosing struct.

Of course, the members still belong to the union, meaning that they share memory space and only one is active at a given time.

Empty Classes and Structs

In C, an empty struct like:

struct A {};

is invalid, whereas in C++, usage like:

struct A {};

or:

class B {};

is perfectly legal. This type of construct is useful when developing a skeleton or placeholder for a class.

An empty class has size greater than zero. Two class objects of empty classes will have distinct addresses, as in:

class A {};

void f()
{
  A* p1 = new A;
  A* p2 = new A;

  // p1 != p2 at this point ...
}

There are still one or two C++ compilers that generate C code as their "assembly" language. To handle an empty class, they will generate a dummy member, so, for example:

class A {};

becomes:

struct A {
  char __dummy;
};

in the C output.

Name Hiding

Consider this small example:

#include <stdio.h>
int xxx[10];
int main()
{
  struct xxx {
    int a;
  };
  printf("%d\n", sizeof(xxx));
  return 0;
}

When compiled as C code, it will typically print a value like 20 or 40, whereas when treated as C++, the output value will likely be 2 or 4. Why is this? In C++, the introduction of the local struct declaration hides the global xxx, and the program is simply taking the size of a struct which has a single integer member in it. In C, sizeof(xxx) refers to the global array, and a tag like xxx doesn't automatically refer to a struct. If we said sizeof(struct xxx), then we would be able to refer to the local struct declaration.

Next month: memory allocation.

 

?Need help? Use our Contacts page.
First posted: 3rd December 1997 efc
Last changed: 3rd December 1997 efc
Issue index
;login: index
USENIX home