Рассмотрим более крупный технический пример, иллюстрирующий ситуацию, в которой имена выходят за пределы области видимости в конце инструкции и блоков (включая тела функций).
// здесь переменные r, i и v не видны
class My_vector {
vector<int> v; // переменная v принадлежит области
// видимости класса
public:
int largest()
{
int r = 0; // переменная r является локальной
// (минимальное неотрицательное целое число)
for (int i = 0; i<v.size(); ++i)
r = max(r,abs(v[i])); // переменная i принадлежит
// области видимости цикла
// здесь переменная i не видна
return r;
}
// здесь переменная r не видна
}
// здесь переменная v не видна
int x; // глобальная переменная — избегайте по возможности
int y;
int f()
{
int x; // локальная переменная, маскирующая глобальную
// переменную x
x = 7; // локальная переменная x
{
int x = y; // локальная переменная x инициализируется
// глобальной переменной y, маскируя локальную
// переменную x, объявленную выше
++x; // переменная x из предыдущей строки
}
++x; // переменная x из первой строки функции f()
return x;
}
Если можете, избегайте ненужных вложений и сокрытий. Помните девиз: “Будь проще!”
Чем больше область видимости имени, тем длиннее и информативнее должно быть ее имя: хуже имен
x
,
y
и
z
для глобальных переменных не придумаешь. Основная причина, по которой следует избегать глобальных переменных, заключается в том, что трудно понять, какие функции изменяют их значения. В больших программах практически невозможно понять, какие функции изменяют глобальную переменную. Представьте себе: вы пытаетесь отладить программу, и выясняется, что глобальная переменная принимает неожиданное значение. Какая инструкция присвоила ей это значение? Почему? В какой функции? Как это узнать?
Функция, присвоившая неправильное значение данной переменной, может находиться в исходном файле, который вы никогда не видели! В хорошей программе может быть лишь несколько (скажем, одна или две) глобальных переменных. Например, калькулятор, описанный в главах 6 и 7, содержит две глобальные переменные: поток лексем
ts
и таблицу символов
names
.
Обратите внимание на то, что большинство конструкций в языке С++ создают вложенные области видимости.
• Функции в классах: функции-члены (раздел 9.4.2).
class C {
public:
void f();
void g() // функция-член может быть определена в классе
{
// ...
}
// ...
void C::f() // определение функции-члена за пределами класса
{
// ...
}
Это наиболее типичный и полезный вариант.
• Классы в других классах: члены-классы (или вложенные классы).
class C {
public:
struct M {
// ...
};
// ...
};
Это допустимо только в сложных классах; помните, что в идеале класс должен быть маленьким и простым.
• Классы в функциях: локальные классы.
void f()
{
class L {
// ...
};
// ...
}
Избегайте таких конструкций; если вам нужен локальный класс, значит, ваша функция слишком велика.
• Функции в других функциях: локальные функции (или вложенные функции).
void f()
{
void g() // незаконно
{
// ...
}
// ...
}
В языке С++ это не допускается; не поступайте так. Компилятор выдаст ошибку.
• Блоки в функциях и других блоках: вложенные блоки.
void f(int x, int y)
{
if (x>y) {
// ...
}
else {
// ...
{
// ...
}
// ...