|
||||
|
R.7 ОписанияОписания используются для интерпретации каждого из идентификаторов; необязательно, чтобы они сопровождались выделением памяти, сопоставляемой с идентификатором. Описания имеют вид описания: спецификации-описания opt список-описателей opt; описание-asm определение-функции спецификация-связи Описатели в списке-описателей (§R.8) содержат описываемые идентификаторы. Конструкция спецификации-описания может отсутствовать только в определении функций (§R.8.3) или в описании функций. Список-описателей может быть пустым, только при описании класса (§R.9) или перечисления (§R.7.2), т.е. когда спецификация-описания есть спецификация-класса или спецификация-перечисления. Конструкция описание-asm объясняется в §R.7.3, а спецификация-связи в §R.7.4. Описание происходит в определенной области видимости (§R.3.2), правила области видимости приводятся в §R.10.4. R.7.1 СпецификацииВ описании можно использовать следующие спецификации: спецификация-описания: спецификация-класса-памяти спецификация-типа спецификация-fct спецификация-шаблона-типа friend typedef спецификации-описания: спецификации-описания opt спецификация-описания Самая длинная последовательность конструкций спецификация-описания, которая, возможно, является именем типа, образует в описании конструкцию спецификации-описания. Последовательность должна быть согласованной, что объясняется ниже. Например, typedef char* Pc; static Pc; // ошибка: нет имени Здесь описание static Pc является незаконным, поскольку не указано никакого имени статической переменной типа Pc. Чтобы иметь переменную типа int с именем Pc, необходимо задать спецификацию-типа int, чтобы показать, что (пере)определяется имя Pc из typedef, а не просто Pc является одним из элементов последовательности конструкций спецификация-описания, например, void f(const Pc); // void f(char* const) void g(const int Pc); // void g(const int) Укажем, что поскольку signed, unsigned, long и short по умолчанию трактуются как int, конструкция имя-typedef, которая появляется после одной из перечисленных спецификаций типа, должна задавать (пере)определяемое имя, например, void h(unsigned Pc); // void h(unsigned int) void k(unsigned int Pc); // void k(unsigned int) R.7.1.1 Спецификации класса памятиСпецификации класса памяти могут быть такие: спецификация-класса-памяти: auto register static extern Спецификации auto и register могут применяться только для имен объектов, которые описаны в блоке (§R.6.3), или для формальных параметров (§R.8.3). Почти всегда спецификация auto избыточна и используется не часто, так, auto используется, чтобы явно отделить оператор-описание от оператора-выражения (§R.6.2). Описание register является описанием auto, которое подсказывает транслятору, что описываемые переменные будут использоваться достаточно интенсивно. Подсказка может быть проигнорирована, и во многих реализациях она игнорируется в том случае, когда берется адрес переменной. Описание объекта считается определением, если только оно не содержит спецификации extern и инициализации (§R.3.1). Определение приводит к выделению памяти соответствующего размера и выполнению соответствующей инициализации (§R.8.4). Спецификации static и extern могут применяться только к именам объектов или функций или к анонимным объединениям. Внутри блока недопустимы описания функций со спецификацией static или формальных параметров со спецификацией static или extern. Статические члены класса описываются в §R.9.4. Спецификация extern недопустима для членов класса. Имя со спецификацией static подлежит внутреннему связыванию. Объекты, описанные как const, подлежат внутреннему связыванию, если только они не были описаны с внешней связью. Имя со спецификацией extern подлежит внешнему связыванию, если только ранее оно не было описано с внутренней связью. Имя с файловой областью видимости и без спецификации-класса-памяти подлежит внешнему связыванию, если только ранее оно не было описано с внутренней связью или со спецификацией const. В смысле связывания для функций, не являющихся членами, спецификация inline эквивалентна static (§R.3.3). Для одного имени все его спецификации, определяющие связывание, должны быть согласованы. Например, static char* f(); // f() имеет внутреннее связывание char* f() // f() все еще внутреннее {/*… */} char* g(); // g() имеет внешнее связывание static char* g() // ошибка: противоречие в связывании {/*… */} static int a; // `a' имеет внутреннее связывание int a; // ошибка: второе определение static int b; // `b' имеет внутреннее связывание extern int b; // `b' все еще внутреннее int c; // `c' имеет внешнее связывание static int c; // ошибка: противоречие в связывании extern int d; // `d' имеет внешнее связывание static int d; // ошибка: противоречие в связывании Имя неопределенного класса можно использовать в описании extern. Однако, такое описание нельзя использовать прежде, чем класс будет определен, например, struct S; extern S a; extern S f(); extern void g(S); void h() { g(a); // ошибка: S неопределено f(); // ошибка: S неопределено } R.7.1.2 Спецификации функцийНекоторые спецификации можно использовать только в описании функций. спецификация-fct: inline virtual Спецификация inline подсказывает транслятору, что необходимо произвести подстановку тела функции вместо обычной реализации вызова функции. Подсказка может игнорироваться. В случае функций, не являющихся членами, спецификация inline дополнительно устанавливает для функции внутреннее связывание (§R.3.3). Функция (§R.5.2.2, §R.8.2.5), определенная в описании класса, имеет по умолчанию спецификацию inline. Функция-член со спецификацией inline должна иметь в точности такое же определение в каждой единице трансляции, где она появляется. Функцию-член не обязательно явно описывать со спецификацией inline при описании класса, чтобы она трактовалась как подстановка. Если спецификации inline не было, связывание будет внешним, если только определение со спецификацией inline не появится перед первым вызовом функции. class X { public: int f(); inline int g(); // X::g() имеет внутреннее связывание int h(); }; void k(X* p) { int i = p-›f(); // теперь X::f() внешнее связывание int j = p-›g(); //… } inline int X::f() // ошибка: вызов до определения // как inline { //… } inline int X::g() { //… } inline int X::h() // теперь X::h() имеет внутреннее связывание { //… } Спецификация virtual может использоваться только в описаниях нестатических функций-членов при описании класса (см. §R.10.2). R.7.1.3 Спецификация typedefОписания со спецификацией typedef задают идентификаторы, которые позднее могут использоваться для обозначения основных или производных типов. Спецификация typedef недопустима в определении-функции (§R.8.3). имя-typedef: идентификатор В пределах области видимости (§R.3.2) описания typedef любой идентификатор, появляющийся в части любого из описателей, становится синтаксически эквивалентным служебному слову и обозначает тип, связанный с данным идентификатором, как описано в §R.8. Таким образом, имя-typedef является синонимом другого типа. В отличие от описания класса (§R.9.1) имя-typedef не добавляет нового типа. Например, после описания typedef int MILES, *KLICKSP; конструкции MILES distance; extern KLICKSP metricp; являются законными описаниями, тип distance есть int, а у metricp тип "указатель на int". С помощью typedef можно переопределить имя так, чтобы оно опять обозначало тип, на который уже ссылалось, причем даже в той области видимости, в которой тип был первоначально описан, например, typedef struct s {/*… */} s; typedef int I; typedef int I; typedef I I; Безымянный класс, который определяется в typedef, получает в качестве своего имени имя, использованное в typedef, например, typedef struct {/*… */} S; // имя структуры стало S С помощью описания typedef нельзя переопределить имя типа, описанного в этой же области видимости, так, чтобы оно обозначало другой тип, например, class complex {/*… */}; typedef int complex; // ошибка: переопределение Аналогично, нельзя описывать класс с именем типа, описанного в этой же области видимости, так, чтобы он обозначал другой тип, например, typedef int complex; class complex {/*… */}; // ошибка: переопределение Имя-typedef, которое обозначает класс, является именем-класса (§R.9.1). Синоним нельзя использовать после следующих префиксов: class, struct и union, а также в именах конструкторов и деструкторов в описании самого класса, например, struct S { S(); ~S(); }; typedef struct S T; S a = T(); // нормально struct T* p; // ошибка R.7.1.4 Спецификация шаблона типаСпецификация шаблона типа используется для задания семейства типов или функций (см. §R.14). R.7.1.5 Спецификация friendСпецификация friend используется для задания доступа к членам класса (см. §R.11.4). R.7.1.6 Спецификация типаК спецификации типа относятся: спецификация-типа: имя-простого-типа спецификация-класса спецификация-перечисления спецификация-сложного-типа :: имя-класса const volatile При описании объекта служебные слова const и volatile можно добавить к любой законной спецификации-типа. Во всех других случаях в описании может присутствовать не более одной спецификации-типа. Объект со спецификацией const можно инициализировать, но его значение не должно изменяться в дальнейшем. Объект со спецификацией const, если только он не был явно описан как extern, не подлежит внешнему связыванию и должен инициализироваться (§R.8.4, §R.12.1). Целое со спецификацией const, инициализированное выражением-константой, может использоваться в выражении-константе (§R.5.19). Каждый элемент массива со спецификацией const имеет ту же спецификацию, а каждый нестатический член, не являющийся функцией, из объекта класса со спецификацией const сам считается const (§R.9.3.1). Объект типа без конструктора или деструктора, который имеет спецификацию const, может быть помещен в память, доступную только по чтению. Попытка записи в любую часть такого объекта или приведет к особой адресной ситуации, или пройдет бесследно, как если бы объект не имел спецификации const. Не существует не зависящего от реализации объяснения объектов со спецификацией volatile. Она служит подсказкой транслятору избегать слишком активной оптимизации, связанной с этим объектом, поскольку значение объекта может изменяться способами, скрытыми от транслятора. Каждый элемент массива со спецификацией volatile имеет ту же спецификацию и каждый нестатический член, не являющийся функцией, из объекта класса со спецификацией volatile сам считается volatile (§R.9.3.1). Если спецификация-типа отсутствует в описании, она считается заданной как int. имя-простого-типа: полное-имя-класса уточненное-имя-типа char short int long signed unsigned float double void Вместе с int нельзя задавать более одного служебного слова long или short. Они могут использоваться и поодиночке, тогда считается, что тип есть int. Служебное слово long может появиться вместе с double. Вместе с char, short, int или long нельзя задавать более одного служебного слова signed или unsigned. Они могут использоваться и поодиночке, тогда считается, что тип есть int. Спецификация signed указывает, что объекты типа char и битовые поля являются знаковыми, для других целочисленных типов эта спецификация избыточна. Конструкции спецификация-класса и спецификация-перечисления определяются в §R.9 и §R.7.2 соответственно. спецификация-сложного-типа: служебное-слово-класса имя-класса служебное-слово-класса идентификатор служебное-слово-класса: class struct union Если задан идентификатор, спецификация-сложного-типа описывает его как имя-класса (см. §R.9.1). Если определено имя, которое описывается с помощью спецификации union, то оно должно быть определено как объединение. Если определено имя, которое описывается с помощью спецификации class, то оно должно быть определено с помощью спецификаций class или struct. Если определено имя, которое описывается с помощью спецификации struct, оно должно быть определено с помощью спецификации class или struct. Имена вложенных типов (§R.9.7) должны уточняться именем объемлющего класса: уточненное-имя-типа: имя-typedef имя-класса :: уточненное-имя-типа полное-имя-класса: уточненное-имя-класса :: уточненное-имя-класса уточненное-имя-класса: имя-класса имя-класса :: уточненное-имя-класса Имя, уточненное именем-класса должно быть типом, определенным в этом классе или в базовом классе этого класса. Как обычно, имя, описанное в производном классе, делает невидимыми члены с этим именем из базовых классов (см. §R.3.2). R.7.2 Описание перечисленияПеречисление является отдельным целочисленным типом (§R.3.6.1) с константами-именами. Его имя в своей области видимости становится конструкцией имя-перечисления, т.е. служит зарезервированным словом. имя-перечисления: идентификатор спецификация-перечисления: enum идентификатор opt { список-перечисления } список-перечисления: элемент-перечисления список-перечисления, элемент-перечисления элемент-перечисления: идентификатор идентификатор = выражение-константа Все идентификаторы из списка-перечисления считаются описанными как константы и могут появляться всюду, где требуются константы. Если не было элементов перечисления с =, то значения констант начинаются с нуля и последовательно увеличиваются на единицу по мере продвижения в списке слева направо. Если элемент перечисления встретился с =, то его идентификатор принимает заданное значение, а последующие идентификаторы без инициализирующей части будут получать возрастающие значения, начиная с заданного. Значение элемента перечисления должно быть типа int или значением, которое можно привести к int с помощью стандартных целочисленных преобразований (§R.4.1). Имена элементов перечисления должны быть отличны от имен обычных переменных и других элементов перечисления той же области видимости. Значения элементов перечисления не обязаны отличаться друг от друга. Считается, что элемент перечисления описан с момента появления его идентификатора или инициализирующего значения, (если оно есть). Например, в определениях enum {a, b, c=0}; enum {d, e, f=e+2}; значения a, c, и d заданы как 0, b и e как 1, а f как 3. Каждое перечисление является целочисленным типом, который отличен от всех других целочисленных типов. Типом элемента перечисления считается данное перечисление. Значение элемента перечисления или объекта типа перечисления преобразуется к целому с помощью стандартных целочисленных преобразований (§R.4.1). Например, в следующем фрагменте: enum color {red, yellow, green=20, blue}; color col = red; color* cp =&col; if (*cp == blue) //… color задан как целочисленный тип, описывающий разные цвета, col описан как объект этого типа, а cp как указатель на объект этого типа. Возможными значениями объекта типа color являются red, yellow, green, blue. Эти значения можно преобразовать в целые значения 0, 1, 20 и 21. Поскольку каждое перечисление - это отдельный тип, объекту типа color можно присваивать только значения типа color, например, color c = 1; // ошибка: несоответствие типов // нет преобразования от int в color int i = yellow; // нормально: yellow преобразуется в int со значением 1 // стандартное целочисленное преобразование Обратитесь также к §R.18.3. Элементы перечисления, определенные в классе (§R.9), относятся к области видимости этого класса, и к ним можно обращаться извне функций-членов этого класса только с помощью явного уточнения именем класса (§R.5.1). Имя самого типа перечисления локально в этом классе (§R.9.7), например, class X { public: enum direction {left='l', right='r'}; int f(int i) { return i==left ? 0 : i==right ? 1 : 2; } }; void g(X* p) { direction d; // ошибка: `direction' вне int i; // области видимости i = p-›f(left); // ошибка: `left' тоже невидим i = p-›f(X::right); // нормально //… } R.7.3 Описания asmОписание asm имеет вид: описание-asm: asm ( строка-литерал ); Назначение описания asm определяется реализацией. Обычно оно используется для передачи информации от транслятора к ассемблеру. R.7.4 Спецификации связиС помощью спецификации-связи можно связать (§R.3.3) фрагменты программ на C++ и на другом языке: спецификация-связи: extern строка-литерал { список-описаний opt } extern строка-литерал описание список-описаний: описание список-описаний описание Требуемое связывание задается с помощью строки-литерала. Ее назначение определяется реализацией. Но во всех реализациях должно быть предусмотрено связывание с функцией на языке C ("C") и с функцией на языке C++ ("C++"). По умолчанию связывание задается как "C++", например, complex sqrt(complex); // по умолчанию связывание с C++ extern "C" { double sqrt(double); // связывание с C } Спецификации связи могут быть вложенными. Спецификация связи не задает область видимости. Спецификация-связи может встретиться только в файловой области видимости (§R.3.2). Спецификация-связи для класса относится к объектам, описанным в нем, и функциям, не являющимся членами. Спецификация-связи, относящаяся к некоторой функции, относится и ко всем объектам и функциям, описанным в ней. Описание связи, содержащее неизвестную для реализации строку, считается ошибочным. Если функция имеет более одной спецификации-связи, то они должны быть согласованы, т.е. задавать одну и ту же строку-литерал. Описание функции без указания спецификации-связи не должно предшествовать первому указанию спецификации связи для этой функции. Функция может быть описана без указания спецификации связи даже после явного указания спецификации связи, но связывание, явно заданное в более раннем описании, не будет устранено таким описанием функции. Из множества перегруженных функций (§R.13) с данным именем не более одной может иметь связывание с языком C, см. §R.7.4. Связывание можно установить для объектов, например: extern "C" { //… _iobuf_iob[_NFILE]; //… int _flsbuf(unsigned,_iobuf*); //… } Когда задается спецификация связи, то функции и объекты можно описать как статические внутри { }. Для таких функций или объектов команда связывания игнорируется. Иначе, функция, описанная при задании связи, трактуется, как если бы она была явно описана как extern, например, ниже второе описание ошибочно (§R.7.1.1): extern "C" double f(); static double f(); // ошибка Объект, описанный внутри конструкции extern "C" {/*… */} все же считается определенным, а не просто описанным. Связывание объектов на C++ с объектами, определенными на других языках, так же как и обратное связывание, зависит от языков и реализации. Такое связывание возможно только в том случае, когда алгоритмы размещения объектов в памяти являются достаточно схожими для двух языков. Если для задания связи в строке-литерале из спецификации-связи используется имя языка программирования, то рекомендуется, чтобы написание этого имени копировалось из документа, определяющего данный язык, например, Ada (а не ADA) и Fortran (а не FORTRAN). |
|
||
Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх |
||||
|