Спецификатор typedef вводит синоним для существующего типа.
Например:
typedef int integer;
typedef const char* pchar;
...
integer a = 10;
pchar s = "abcd";
С помощью typedef невозможно переопределить тип, разрешено только вводить новые имена-синонимы.
Таким образом:
struct A {};
typedef int A; // error
Основные отличия от #define:
#define является директивой препроцессора и, как следствие, обрабатывается перед компиляцией путем простой замены всех вхождений. К чему это может привести, иллюстрирует следующий пример:
#define P_INT int*
typedef int* p_integer;
// эквивалентно
P_INT p1, p2; // int* p1, int p2;
p_integer p3, p4; // int* p3, int* p4;
int* a;
p1 = a; // ok int* p1
p2 = a; // error int p1
p3 = a; // ok int* p3
p4 = a; // ok int* p4
На первый взгляд тип и синоним типа выглядят одинаково, но при использовании typedef несколько иначе обрабатываются cv-квалификаторы(в отличие от #define):
typedef char* pchar;
#define PCHAR char*
const pchar a; // char* const
const PCHAR b; // const char* == char const* != char* const
PCHAR const c; // char* const
С помощью #define нельзя объявить имя функции или массива, но можно используя typedef. Синтаксис объявления массива таков
typedef int int_array[10];
Следует заметить что так неправильно:
typedef int[10] int_array; // error
Теперь о функциях...
В связи с тем, что typedef не вводит нового типа следующий код будет ошибочным:
void f(int a) { ... }
void f(integer a) { ... } // error - функция с такой сигнатурой
// и именем уже существует
Рассмотрим как создаются типы-синонимы для функций:
Для функции вида void f1(int);
объявлением нового синонима служит следующая запись
typedef void(*pfn_f)(int);
где pfn_f имя синонима. Вызов этой функции с использование нового имени происходит просто:
pfn_f pf = f1; // Создали переменную 'указатель на функцию' и
// присвоили ей значение
pf(10); // Произвели вызов
Еще несколько примеров:
void f1(int);
void f2(int);
typedef void(*pfn_f)(int);
int f3(int, char*, double);
typedef int(*pfn_f3)(int, char*, double);
int main()
{
pfn_f pf = f1;
pf(10); // f1(int)
pf = f2;
pf(1); // f2(int)
pfn_f3 pf2 = f3;
pf2(10, "abc", 12.4); // f3(int, char*, double)
return 0;
}
typedef'ы широко применяются в WinApi для CALLBACK функций, а также для типов.
Еще одним отличием служит область видимости, для typedef'a ограниченная функцией, классом, или пространством имен.
В заключении можно добавить что производные классы(в т.ч. шаблонные) наследуют типы объявленные с помощью typedef'ов в базовых классах.