Б. Страуструп - Язык программирования С++ (1119446), страница 17
Текст из файла (страница 17)
В этой главе лишь упоминаются средствадля определения пользовательских типов и операций над ними. Они обсуждаются в главах 5 и 7.2.1 ОПИСАНИЯИмя (идентификатор) следует описать прежде, чем оно будет использоваться в программе на С++.Это означает, что нужно указать его тип, чтобы транслятор знал, к какого вида объектам относится имя.Ниже приведены несколько примеров, иллюстрирующих все разнообразие описаний:char ch;int count = 1;char* name = "Njal";struct complex { float re, im; };complex cvar;extern complex sqrt(complex);extern int error_number;typedef complex point;float real(complex* p) { return p->re; };const double pi = 3.1415926535897932385;struct user;template<class T> abs(T a) { return a<0 ? -a : a; }enum beer { Carlsberg, Tuborg, Thor };Из этих примеров видно, что роль описаний не сводится лишь к привязке типа к имени.
Большинствоуказанных описаний одновременно являются определениями, т.е. они создают объект, на которыйссылается имя. Для ch, count, name и cvar таким объектом являетсяэлемент памятисоответствующего размера. Этот элемент будет использоваться как переменная, и говорят, что длянего отведена память. Для real подобным объектом будет заданная функция. Для константы piобъектом будет число 3.1415926535897932385. Для complex объектом будет новый тип.
Для pointобъектом является тип complex, поэтому point становится синонимом complex. Следующие описанияуже не являются определениями:extern complex sqrt(complex);extern int error_number;struct user;Это означает, что объекты, введенные ими, должны быть определены где-то в другом местепрограммы. Тело функции sqrt должно быть указано в каком-то другом описании. Память дляпеременной error_number типа int должна выделяться в результате другого описания error_number.Должно быть и какое-то другое описание типа user, из которого можно понять, что это за тип. Впрограмме на языке С++ должно быть только одно определение каждого имени, но описаний можетбыть много.
Однако все описания должны быть согласованы по типу вводимого в них объекта. Поэтомув приведенном ниже фрагменте содержатся две ошибки:int count;int count;// ошибка: переопределениеextern int error_number;extern short error_number; // ошибка: несоответствие типов46Бьерн Страуструп.Язык программирования С++Зато в следующем фрагменте нет ни одной ошибки (об использовании extern см. #4.2):extern int error_number;extern int error_number;В некоторых описаниях указываются "значения" объектов, которые они определяют:struct complex { float re, im; };typedef complex point;float real(complex* p) { return p->re };const double pi = 3.1415926535897932385;Для типов, функций и констант "значение" остается неизменным; для данных, не являющихсяконстантами, начальное значение может впоследствии изменяться:int count = 1;char* name = "Bjarne";//...count = 2;name = "Marian";Из всех определений только следующее не задает значения:char ch;Всякое описание, которое задает значение, является определением.2.1.1 Область видимостиОписанием определяется область видимости имени.
Это значит, что имя может использоваться тольков определенной части текста программы. Если имя описано в функции (обычно его называют"локальным именем"), то область видимости имени простирается от точки описания до конца блока, вкотором появилось это описание.
Если имя не находится в описании функции или класса (его обычноназывают "глобальным именем"), то область видимости простирается от точки описания до концафайла, в котором появилось это описание. Описание имени в блоке может скрывать описание вобъемлющем блоке или глобальное имя; т.е. имя может быть переопределено так, что оно будетобозначать другой объект внутри блока. После выхода из блока прежнее значение имени (если онобыло) восстанавливается. Приведем пример:int x;// глобальное xvoid f(){int x;// локальное x скрывает глобальное xx = 1;// присвоить локальному x{int x;// скрывает первое локальное xx = 2;// присвоить второму локальному x}x = 3;// присвоить первому локальному x}int* p = &x; // взять адрес глобального xВ больших программах не избежать переопределения имен. К сожалению, человек легко можетпроглядеть такое переопределение.
Возникающие из-за этого ошибки найти непросто, возможнопотому, что они достаточно редки. Следовательно, переопределение имен следует свести к минимуму.Если вы обозначаете глобальные переменные или локальные переменные в большой функции такимиименами, как i или x, то сами напрашиваетесь на неприятности.Есть возможность с помощьюоперации разрешения области видимости :: обратиться к скрытому глобальному имени, например:int x;void f2(){int x = 1;::x = 2;// скрывает глобальное x// присваивание глобальному x47Бьерн Страуструп.Язык программирования С++}Возможность использовать скрытое локальное имя отсутствует.Область видимости имени начинается в точке его описания (по окончании описателя, но еще до началаинициализатора - см.
$$R.3.2). Это означает, что имя можно использовать даже до того, как задано егоначальное значение. Например:int x;void f3(){int x = x;}// ошибочное присваиваниеТакое присваивание недопустимо и лишено смысла. Если вы попытаетесь транслировать эту программу,то получите предупреждение: "использование до задания значения". Вместе с тем, не применяя оператора::, можно использовать одно и то же имя для обозначения двух различных объектов блока. Например:int x = 11;void f4()// извращенный пример{int y = x;// глобальное xint x = 22;y = x;// локальное x}Переменная y инициализируется значением глобального x, т.е. 11, а затем ей присваивается значениелокальной переменной x, т.е. 22.
Имена формальных параметров функции считаются описанными всамом большом блоке функции, поэтому в описании ниже есть ошибка:void f5(int x){int x;}// ошибкаЗдесь x определено дважды в одной и той же области видимости. Это хотя и не слишком редкая, нодовольно тонкая ошибка.2.1.2 Объекты и адресаМожно выделять память для "переменных", не имеющих имен, и использовать эти переменные.Возможно даже присваивание таким странно выглядящим "переменным", например, *p[a+10]=7.Следовательно, есть потребность именовать "нечто хранящееся в памяти". Можно привестиподходящую цитату из справочного руководства: "Любой объект - это некоторая область памяти, аадресом называется выражение, ссылающееся на объект или функцию" ($$R.3.7).
Слову адрес (lvalue left value, т.е. величина слева) первоначально приписывался смысл "нечто, что может в присваиваниистоять слева". Адрес может ссылаться и на константу (см. $$2.5). Адрес, который не был описан соспецификацией const, называется изменяемым адресом.2.1.3 Время жизни объектовЕсли только программист не вмешается явно, объект будет создан при появлении его определения иуничтожен, когда исчезнет из области видимости. Объекты с глобальными именами создаются,инициализируются (причем только один раз) и существуют до конца программы.
Если локальныеобъекты описаны со служебным словом static, то они также существуют до конца программы.Инициализация их происходит, когда в первый раз управление "проходит через" описание этихобъектов, например:int a = 1;void f(){int b = 1;static int c = a;cout << " a = " << a++// инициализируется при каждом вызове f()// инициализируется только один раз48Бьерн Страуструп.Язык программирования С++<< " b = " << b++<< " c = " << c++ << '\n';}int main(){while (a < 4) f();}Здесь программа выдаст такой результат:a = 1 b = 1 c = 1a = 2 b = 1 c = 2a = 3 b = 1 c = 3''Из примеров этой главы для краткости изложения исключена макрокоманда #include <iostream>.
Онанужна лишь в тех из них, которые выдают результат.Операция "++" является инкрементом, т. е. a++ означает: добавить 1 к переменной a.Глобальная переменная или локальная переменная static, которая не была явно инициализирована,инициализируется неявно нулевым значением (#2.4.5). Используя операции new и delete, программистможет создавать объекты, временем жизни которых он управляет сам (см. $$3.2.6).2.2 ИМЕНАИмя (идентификатор) является последовательностью букв или цифр. Первый символ должен бытьбуквой.
Буквой считается и символ подчеркивания _. Язык С++ не ограничивает число символов вимени. Но в реализацию входят программные компоненты, которыми создатель транслятора управлятьне может (например, загрузчик), а они, к сожалению, могут устанавливать ограничения. Кроме того,некоторые системные программы, необходимые для выполнения программы на С++, могут расширятьили сужать множество символов, допустимых в идентификаторе.