Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп
Шрифт:
Интервал:
Закладка:
A.5.7. Операторы приведения
Существуют четыре оператора приведения к типу.
Динамическое приведение обычно используется для навигации по иерархии классов, если указатель p — указатель на базовый класс, а класс D — производный от базового класса. Если операнд v не относится к типу D*, то эта операция возвращает число 0. Если необходимо, чтобы операция dynamic_cast в случае неудачи не возвращала 0, а генерировала исключение bad_cast, то ее следует применять к ссылкам, а не к указателям. Динамическое приведение — единственное приведение, опирающееся на проверку типов во время выполнения программы.
Статическое приведение используется для “разумных преобразований,” т.е. если операнд v может быть результатом неявного преобразования типа T (см. раздел 17.8).
Оператор reinterpret_cast используется для реинтерпретации комбинации битов. Его переносимость не гарантируется. Фактически лучше считать, что он является вообще не переносимым. Типичным примером реинтерпретации является преобразование целого числа в указатель, чтобы получить машинный адрес в программе (см. разделы 17.8 и 25.4.1).
Приведения в стиле языка С и функциональные приведения могут выполнить любое преобразование типа, которое можно осуществить с помощью оператора static_cast или reinterpret_cast в сочетании с оператором const_cast.
Приведений лучше избегать. Во многих ситуациях их использование свидетельствует о плохом стиле программирования. Исключения из этого правила представлены в разделах 17.8 и 25.4.1. Приведение в стиле языка С и функциональные приведения имеют ужасное свойство: они позволяют вам не вникать в то, что именно они делают (см. раздел 27.3.4). Если вы не можете избежать явного преобразования типа, лучше используйте именованные приведения.
A.6. Инструкции
Грамматическое определение инструкций языка C++ приведено ниже (opt означает “необязательный”).
инструкция:
объявление
{ список_инструкции opt }
try { список_инструкции opt } список_обработчиков
выражение opt;
инструкция_выбора
инструкция_итерации
инструкция_с_метками
управляющая_инструкция
инструкция_выбора:
if (условие) инструкция
if (условие) инструкция else инструкция
switch (условие) инструкция
инструкция_итерации:
while (условие) инструкция
do инструкция while (выражение);
for (инструкция_инициализации_for условие opt; выражение opt) инструкция
инструкция_с_метками:
case константное_выражение: инструкция
default: инструкция
identifier: инструкция
управляющая_инструкция:
break;
continue;
return выражение opt;
goto идентификатор;
список_инструкции:
инструкция список_инструкции opt
условие:
выражение
спецификатор_типа объявляемый_объект = выражение
инструкция_инициализации_for:
выражение opt;
спецификатор_типа объявляемый_объект = выражение;
список_обработчиков:
catch (объявление_исключения) { список_инструкции opt }
список_обработчиков список_обработчиков opt
Обратите внимание на то, что объявление — это инструкция, а присваивание и вызов функции являются выражениями. К этому определению следует добавить следующий список.
• Итерация (for и while); см. раздел 4.4.2.
• Ветвление (if, switch, case и break); см. раздел 4.4.1. Инструкция break прекращает выполнение ближайшей вложенной инструкции switch, while, do или for. Иначе говоря, следующей будет выполнена инструкция, следующая за последней в теле одной из перечисленных выше инструкций.
• Выражения; см. разделы A.5 и 4.3.
• Объявления; см. разделы A.6 и 8.2.
• Исключения (try и catch); см. разделы 5.6 и 19.4.
Рассмотрим пример, созданный просто для того, чтобы продемонстрировать разнообразие инструкций (какую задачу они решают?).
int* f(int p[],int n)
{
if (p==0) throw Bad_p(n);
vector<int> v;
int x;
while (cin>>x) {
if (x==terminator) break; // выход из цикла while
v.push_back(x);
}
for (int i = 0; i<v.size() && i<n; ++i) {
if (v[i]==*p)
return p;
else
++p;
}
return 0;
}
A.7. Объявления
Объявление (declaration) состоит из трех частей:
• имя объявляемой сущности;
• тип объявляемой сущности;
• начальное значение объявляемой сущности (во многих случаях необязательное).
Мы можем объявлять следующие сущности:
• объекты встроенных типов и типов, определенных пользователем (раздел A.8);
• типы, определенные пользователем (классы и перечисления) (разделы A.10–А.11, глава 9);
• шаблоны (шаблонные классы и функции) (раздел A.13);
• альтернативные имена (раздел A.16);
• пространства имен (разделы A.15 и 8.7);
• функции (включая функции-члены и операторы) (раздел A.9, глава 8);
• перечисления (значения перечислений) (разделы A.11 и 9.5);
• макросы (разделы A.17.2 и 27.8).
A.7.1. Определения
Определение с инициализацией, резервирующее область памяти или как-то иначе поставляющую компилятору всю информацию, необходимую для использования имени в программе, называется определением (definition). Каждый тип, объект и функция в программе должны иметь только одно определение. Рассмотрим примеры.
double f(); // объявление
double f() { /* ... */ }; // также определение
extern const int x; // объявление
int y; // также определение
int z = 10; // определение с явной инициализацией
Константы должны быть инициализированы. Для этого используется инициализатор, если константа не объявлена с помощью ключевого слова extern (в таком случае инициализатор вместе с определением должны быть расположены в другом месте) или если константа не имеет тип, имеющий конструктор по умолчанию (раздел A.12.3). Константные члены класса должны инициализироваться в каждом конструкторе с помощью инициализатора (раздел A.12.3).
A.8. Встроенные типы
Язык C++ имеет много фундаментальных типов и типов, составленных из фундаментальных типов с помощью модификаторов.
Здесь T означает “некий тип”, поэтому существуют варианты long unsigned int, long double, unsigned char и const char* (указатель на константный символ char). Однако эта система не совсем полная; например, в ней нет типа short double (его роль играет тип float); типа signed bool (совершенно бессмысленного); типа short long int (это было бы лишним) и типа long long long long int. Некоторые компиляторы в ожидании стандарта C++0x допускают тип long long int (читается как “очень длинный целый тип ”). Гарантируется, что тип