Чтение онлайн

на главную

Жанры

Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:

8.6.1. Вычисление выражения

Порядок вычисления подвыражений управляется правилами, которые больше ориентируются на оптимизацию кода, чем на удобство работы программиста. Это неудобно, но в любом случае следует избегать чрезмерно сложных выражений. Помните простое правило: если изменяете значение переменной в выражении, то не используйте его дважды в одном и том же выражении. Рассмотрим пример.

v[i] = ++i; // неопределенный
порядок вычислений

v[++i] = i; // неопределенный порядок вычислений

int x = ++i + ++i; // неопределенный порядок вычислений

cout << ++i << ' ' << i << '\n'; // неопределенный порядок вычислений

f(++i,++i); // неопределенный порядок вычислений

К сожалению, не все компиляторы выдают предупреждение о таких ошибках; это плохо, потому что нельзя рассчитывать на то, что результаты будут одинаковыми при выполнении вычислений на другом компьютере, при использовании других компиляторов или при других установках оптимизатора.

Компиляторы действительно по-разному обрабатывают этот код; избегайте таких ситуаций.

Обратите внимание на то, что оператор

=
(присваивание) в выражениях используется наряду с остальными, поэтому нет никакой гарантии того, что левая часть оператора будет вычислена раньше правой части. По этой причине выражение
v[++i] = i
имеет неопределенный результат.

8.6.2. Глобальная инициализация

Глобальные переменные (и переменные из пространства имен; раздел 8.7) в отдельной единице трансляции инициализируются в том порядке, в котором они появляются. Рассмотрим пример.

// файл f1.cpp

int x1 = 1;

int y1 = x1+2; // переменная y1 становится равной 3

Эта инициализация логически происходит до выполнения кода в функции

main
. Использование глобальной переменной, за исключением редких ситуаций, нецелесообразно. Мы уже говорили, что не существует эффективного способа, позволяющего программисту определить, какие части программы считывают или записывают переменную (см. раздел 8.4). Другая проблема заключается в том, что порядок инициализации глобальных переменных не определен. Рассмотрим пример.

// файл f2.cpp

extern int y1;

int y2 = y1+2; // переменная y2 становится равной 2 или 5

Такой код нежелателен по нескольким причинам: в нем используются глобальные переменные, которые имеют слишком короткие имена, и сложная инициализация глобальных переменных. Если глобальные переменные в файле

f1.cpp
инициализируются до глобальных переменных в файле
f2.cpp
, то переменная
y2
будет инициализирована числом
5
(как наивно ожидает программист).

Однако, если глобальные переменные в файле

f2.cpp
инициализируются до глобальных переменных в файле
f1.cpp
, переменная
y2
будет инициализирована числом
2
(поскольку память, используемая для глобальных
переменных, инициализируется нулем до попытки сложной инициализации). Избегайте этого и старайтесь не использовать нетривиальную инициализацию глобальных переменных; любой инициализатор, отличающийся от константного выражения, следует считать сложным.

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

Date
.

const Date default_date(1970,1,1); // дата по умолчанию: 1 января 1970

Как узнать, что переменная

default_date
никогда не использовалась до своей инициализации? В принципе мы не можем этого знать, поэтому не должны писать это определение. Чаще всего для проверки используется вызов функции, возвращающей некое значение. Рассмотрим пример.

const Date default_date // возвращает дату по умолчанию

{

return Date(1970,1,1);

}

Эта функция создает объект типа

Date
каждый раз, когда вызывается функция
default_date
. Часто этого вполне достаточно, но если функция
default_date
вызывается часто, а создание объекта класса Date связано с большими затратами, предпочтительнее было бы конструировать его только один раз. В таком случае код мог бы выглядеть так:

const Date& default_date

{

static const Date dd(1970,1,1); // инициализируем dd

// только при первом вызове

return dd;

}

Статическая локальная переменная инициализируется (создается) только при первом вызове функции, в которой она объявлена. Обратите внимание на то, что мы вернули ссылку, чтобы исключить ненужное копирование, и, в частности, вернули константную ссылку, чтобы предотвратить несанкционированное изменение значения аргумента при вызове функции. Рассуждения о передаче аргумента (см. раздел 8.5.6) относятся и к возвращаемому значению.

8.7. Пространства имен

Для организации кода в рамках функции используются блоки (см. раздел 8.4).

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

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

• Позволяют именовать то, что мы определили.

Нам нужно иметь средство для организации классов, функций, данных и типов в виде идентифицируемой и именованной части программы, не прибегая к определению типа. Языковый механизм, позволяющий осуществить такую группировку объявлений, называют пространством имен (namespace). Например, мы можем создать графическую библиотеку с классами

Color
,
Shape
,
Line
,
Function
и
Text
(глава 13).

Поделиться:
Популярные книги

Черный Маг Императора 13

Герда Александр
13. Черный маг императора
Фантастика:
попаданцы
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Черный Маг Императора 13

Последняя Арена 4

Греков Сергей
4. Последняя Арена
Фантастика:
рпг
постапокалипсис
5.00
рейтинг книги
Последняя Арена 4

Маяк надежды

Кас Маркус
5. Артефактор
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Маяк надежды

Великий перелом

Ланцов Михаил Алексеевич
2. Фрунзе
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Великий перелом

Сопротивляйся мне

Вечная Ольга
3. Порочная власть
Любовные романы:
современные любовные романы
эро литература
6.00
рейтинг книги
Сопротивляйся мне

Инквизитор Тьмы 2

Шмаков Алексей Семенович
2. Инквизитор Тьмы
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Инквизитор Тьмы 2

Мастер Разума V

Кронос Александр
5. Мастер Разума
Фантастика:
городское фэнтези
попаданцы
5.00
рейтинг книги
Мастер Разума V

Бандит 2

Щепетнов Евгений Владимирович
2. Петр Синельников
Фантастика:
боевая фантастика
5.73
рейтинг книги
Бандит 2

Истребители. Трилогия

Поселягин Владимир Геннадьевич
Фантастика:
альтернативная история
7.30
рейтинг книги
Истребители. Трилогия

Гардемарин Ее Величества. Инкарнация

Уленгов Юрий
1. Гардемарин ее величества
Фантастика:
городское фэнтези
попаданцы
альтернативная история
аниме
фантастика: прочее
5.00
рейтинг книги
Гардемарин Ее Величества. Инкарнация

Падение Твердыни

Распопов Дмитрий Викторович
6. Венецианский купец
Фантастика:
попаданцы
альтернативная история
5.33
рейтинг книги
Падение Твердыни

"Дальние горизонты. Дух". Компиляция. Книги 1-25

Усманов Хайдарали
Собрание сочинений
Фантастика:
фэнтези
боевая фантастика
попаданцы
5.00
рейтинг книги
Дальние горизонты. Дух. Компиляция. Книги 1-25

Ох уж этот Мин Джин Хо 2

Кронос Александр
2. Мин Джин Хо
Фантастика:
попаданцы
5.00
рейтинг книги
Ох уж этот Мин Джин Хо 2

Энфис 6

Кронос Александр
6. Эрра
Фантастика:
героическая фантастика
рпг
аниме
5.00
рейтинг книги
Энфис 6