25A. Type Definitions

C++ lets you give a new name to a type using typedef. Declaration

  typedef existingType newName;
makes newName refer to existingType. For example,
  typedef int* IntPtr;
makes IntPtr mean int*. Then, declaration
  IntPtr p;
creates variable p of type int*. You typically make type definitions outside of functions, often in header files.


What typedef means

A type definition does not call for a textual substitution. It treats the new type as atomic, or as a unit. Recall that declaration

  int* p, q;
says that p has type int* and q has type int. But
  IntPtr p, q;
says that p and q both have type IntPtr, which is the same as int*. As you can see, having names for pointer types can make a program clearer.


Const and typedefs

Now comes a surprising part of this discussion.

Const with a typedef 'd pointer type

After declarations

  typedef int* IntPtr;
  const IntPtr p;
you find that you are allowed to change *p but you are not allowed to change p. Types const int* and const IntPtr are different! Since IntPtr is a pointer type, const IntPtr indicates that the pointer itself is a constant.

Sensible const pointer types

Type const IntPtr is usually not what you want, which is a type that is equivalent to const int*. You can get that by doing a separate typedef. Declaration

  typedef const int* ConstIntPtr;
says that ConstIntPtr is equivalent to const int* and
  ConstIntPtr p, q;
has exactly the same meaning as
  const int *p, *q;


Duplicate typedefs

Do not define a type that is already defined. Typically, that is relevant in modules, the topic of the next page. Do not define a type both in a header file stuff.h and in the associated implementation file stuff.cpp. The standards require that.


Understanding const (optional)

A more basic way to use const in C++ is to write const after the part of a type that it modifies.

int const * p;

Here, const immediately follows int. It is the variable of type int that cannot by modified (using p). So, if y has type int, then
  p = &y;
is allowed, but
  *p = 0;
is not.

int * const q = s;

Here, const immediately follows *. It is the pointer that cannot by modified (using q). So, if y has type int, then
  q = &y;
is not allowed, but
  *q = 0;
is allowed.

int const * const r = s;

Here, neither
  r = &y;
nor
  *r = 0;
is allowed.

const int * s;

When const is written before a type, it applies to the first part of the type. So type const int * is the same as int const *.

As you can see, the compiler moves const across the first part of the type.


const IntPtr s;

Here, the compiler moves const across the first part of the type. But that gives type IntPtr const. Replacing IntPtr by int * gives type int * const. It is the pointer that cannot be changed.