const int x = 7; // инициализация с помощью синтаксической
// конструкции =
const int x2(9); // инициализация с помощью синтаксической
// конструкции ()
const int y; // ошибка: нет инициализации
void f(int z)
{
int x; // неинициализированная переменная
// ...здесь нет присваивания значений переменной x...
x = 7; // присваивание значения переменной x
// ...
}
Этот код выглядит вполне невинно, но что будет, если в первом пропущенном фрагменте, отмеченном многоточием, будет использована переменная x
? Рассмотрим пример.
void f(int z)
{
int x; // неинициализированная переменная
// ...здесь нет присваивания значений переменной x...
if (z>x) {
// ...
}
// ...
x = 7; // присваивание значения переменной x
// ...
}
Поскольку переменная x
не инициализирована, выполнение оператора z>x
может привести к неопределенным последствиям. Сравнение z>x
приведет к разным результатам на разных компьютерах и даже на одном и том же компьютере в разных сеансах работы. В принципе оператор z>x
может вызвать прекращение работы программы из-за машинной ошибки, но чаще всего ничего не происходит, и мы получаем непредсказуемые результаты.
Естественно, такое непредсказуемое поведение программы нас не устраивает, но если мы не проинициализируем переменные, то в итоге произойдет ошибка.
Напомним, что “глупые ошибки” (которые происходят при использовании неинициализированных переменных) происходят из-за спешки или усталости. Как правило, компиляторы пытаются предупредить программистов, но в сложных программах — в которых такие ошибки и появляются чаще всего — они не могут выловить все такие ошибки. Существуют люди, не привыкшие инициализировать переменные. Часто это происходит потому, что они учили языки, в которых этого не требовалось; вы можете встретить такие примеры в будущем. Пожалуйста, не усложняйте себе жизнь, забывая инициализировать переменные при их определении.
8.2.3. Инициализация по умолчанию
Возможно, вы заметили, что мы часто не инициализируем объекты классов string
, vector
и т.д. Рассмотрим пример.
vector
string s;
while (cin>>s) v.push_back(s);
Это не противоречит правилу, утверждающему, что переменные перед их использованием должны быть проинициализированы. В данном случае, если мы не задаем начальные значения, происходит инициализация строк и векторов по умолчанию. Таким образом, вектор v
пуст (т.е. не содержит элементов), и строка s
перед входом в цикл также пуста (""
). Механизм, гарантирующий инициализацию по умолчанию, называется
К сожалению, язык С++ не предусматривает инициализацию по умолчанию для встроенных типов. Лишь глобальные переменные (см. раздел 8.4) по умолчанию инициализируются нулем, но их использование следует ограничивать. Большинство полезных переменных, к которым относятся локальные переменные и члены классов, не инициализируются, пока не указано их начальное значение (или не задан конструктор по умолчанию).
Не говорите, что вас не предупреждали!
8.3. Заголовочные файлы
Как управлять объявлениями и определениями? Они должны быть согласованными. В реальных программах могут быть десятки тысяч объявлений; программы с сотнями тысяч объявлений тоже не редкость. Как правило, когда вы пишете программу, большинство используемых определений написано не вами. Например, реализации потока cout
и функции sqrt()
были написаны много лет назад кем-то другим. Мы просто используем их. Главным средством управления сущностями, определенными где-то в другом месте, в языке С++ являются заголовки. В принципе #include
. Например, вы можете решить улучшить организацию исходного кода нашего калькулятора (см. главы 6 и 7), выделив объявления лексем в отдельный файл. Таким образом, можно определить заголовочный файл token.h
, содержащий объявления, необходимые для использования классов Token
и Token_stream
.