Читаем Программирование полностью

Естественно, мы не хотели бы совсем отказываться от защиты, представляемой системой типов, но иногда у нас нет логичной альтернативы (например, когда мы должны обеспечить работу с программой, написанной на другой языке программирования, в котором ничего не известно о системе типов языка С++). Кроме того, существует множество ситуаций, в которых необходимо использовать старые программы, разработанные без учета системы безопасности статических типов.

В таких случаях нам нужны две вещи.

• Тип указателя, ссылающегося на память без учета информации о том, какие объекты в нем размещены.

• Операция, сообщающая компилятору, какой тип данных подразумевается (без проверки) при ссылке на ячейку памяти с помощью такого указателя.

  Тип void* означает “указатель на ячейку памяти, тип которой компилятору неизвестен”. Он используется тогда, когда необходимо передать адрес из одной части программы в другую, причем каждая из них ничего не знает о типе объекта, с которым работает другая часть. Примерами являются адреса, служащие аргументами функций обратного вызова (см. раздел 16.3.1), а также распределители памяти самого нижнего уровня (такие как реализация оператора new).

Объектов типа void не существует, но, как мы видели, ключевое слово void означает “функция ничего не возвращает”.

void v;   // ошибка: объектов типа void не существует

void f(); // функция f() ничего не возвращает;

          // это не значит, что функция f() возвращает объект

          // типа void

Указателю типа void* можно присвоить указатель на любой объект. Рассмотрим пример.

void* pv1 = new int;        // OK: int* превращается в void*

void* pv2 = new double[10]; // OK: double* превращается в void*

Поскольку компилятор ничего не знает о том, на что ссылается указатель типа void*, мы должны сообщить ему об этом.

void f(void* pv)

{

  void* pv2 = pv;  // правильно (тип void* для этого

                   // и предназначен)

  double* pd = pv; // ошибка: невозможно привести тип void*

                   // к double*

  *pv = 7;    // ошибка: невозможно разыменовать void*

              // (тип объекта, на который ссылается указатель,

              // неизвестен)

  pv[2] = 9;                       // ошибка: void* нельзя индексировать

  int* pi = static_cast(pv); // OK: явное приведение

  // ...

}

  Оператор static_cast позволяет явно преобразовать указатели типов в родственный тип, например void* в double* (раздел A.5.7). Имя static_cast — это сознательно выбранное отвратительное имя для отвратительного (и опасного) оператора, который следует использовать только в случае крайней необходимости. Его редко можно встретить в программах (если он вообще где-то используется). Операции, такие как static_cast, называют явным преобразованием типа (explicit type conversion), или просто приведением (cast), потому что в языке C++ предусмотрены два оператора приведения типов, которые потенциально еще хуже оператора static_cast.

• Оператор reinterpret_cast может преобразовать тип в совершенно другой, никак не связанный с ним тип, например int в double*.

• Оператор const_cast позволяет отбросить квалификатор const.

Рассмотрим пример.

Register* in = reinterpret_cast(0xff);

void f(const Buffer* p)

{

  Buffer* b = const_cast(p);

  // ...

}

Первый пример — классическая ситуация, в которой необходимо применить оператор reinterpret_cast. Мы сообщаем компилятору, что определенная часть памяти (участок, начинающийся с ячейки 0xFF) рассматривается как объект класса Register (возможно, со специальной семантикой). Такой код необходим, например, при разработке драйверов устройств.

Во втором примере оператор const_cast аннулирует квалификатор const в объявлении const Buffer* указателя p. Разумеется, мы понимали, что делали.

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже