8. Описания
Описания используются для определения интерпретации, да-
ваемой каждому идентификатору. Они не обязательно резервируют
память, связанную с идентификатором. Описания имеют вид:
описание:
спецификаторы_описания opt список_описателей opt ;
описание_имени
asm_описание
Описатели в списке_описателей содержат идентификаторы,
подлежащие описанию. Спецификаторы_описания могут быть опуще-
ны только в определениях внешних функций (#10) или в описани-
ях внешних функций. Список описателей может быть пустым толь-
ко при описании класса (#8.5) или перечисления (#8.10), то
есть, когда спецификаторы_описания - это class_спецификатор
или enum_спецификатор. Описания имен описываются в #8.8; опи-
сания asm описаны в #8.11.
спецификатор_описания:
спецификатор_класса_памяти
спецификатор_типа
спецификатор_функции
friend
typedef
спецификаторы_описания:
спецификатор_описания спецификатор_описания opt
Список должен быть внутренне непротиворечив в описывае-
мом ниже смысле.
8.1 Спецификаторы Класса Памяти
Спецификаторы - это:
спецификатор_класса_памяти:
auto
static
extern
register
Описания, использующие спецификаторы auto, static и
register также служат определениями тем, что они вызывают ре-
зервирование соответствующего объема памяти. Если описание
extern не является определением (#4.2), то где-то еще должно
быть определение для данных идентификаторов.
Описание register лучше всего представить как описание
auto (автоматический) с подсказкой компилятору, что описанные
переменные усиленно используются. Подсказка может быть проиг-
норирована. К ним не может применяться операция получения ад-
реса &.
параметров.
В описании может быть задан максимум один sc_специфика-
тор. Если в описании отсутсвует спецификатор_класса_памяти,
то класс памяти принимается автоматическим внутри функции и
статическим вне. Исключение: функции не могут быть автомати-
ческими.
Спецификаторы static и extern могут использоваться толь-
ко для имен объектов и функций.
Некоторые спецификаторы могут использоваться только в
описаниях функций:
спецификатор_функции:
overload
inline
virtual
Спецификатор перегрузки overload делает возможным ис-
пользование одного имени для обозначения нескольких функций,
см. #8.9.
Спецификатор inline является только подсказкой компиля-
тору, не влияет на смысл программы и может быть проигнориро-
ван. Он используется, чтобы указать на то, что при вызове
функции inline-подстановка тела функции предпочтительнее
обычной реализацци вызова функции. Функция (#8.5.2 и
#8.5.10), определенная внутри описания класса, является
inline по умолчанию.
Спецификатор virtual может использоваться только в опи-
саниях членов класса, см. #8.5.4.
Спецификатор friend используется для отмены правил сок-
рытия имени для членов класса и может использоваться только
внутри описаний классов, см. #8.5.9.
С помощью спецификатора typedef вводится имя для типа,
см. #8.8.
8.2 Спецификаторы Типа
Спецификаторами типов (спецификатор_типа) являются:
спецификатор_типа:
простое_имя_типа
спецификатор_класса
enum-спецификатор
сложный_спецификатор_типа
const
Слово const можно добавлять к любому допустимому специ-
фикатору_типа. В остальных случаях в описании может быть дано
не более одного спецификатора_типа. Объект типа const не яв-
ляется lvalue. Если в описании опущен спецификатор типа, он
принимается int.
простое_имя_типа:
char
short
int
long
unsigned
float
double
const
void
Слова long, short и unsigned можно рассматривать как
прилагательные. Они могут применяться к типу int; unsigned
может также применяться к типам char, short и long.
Спецификаторы класса и перечисления обсуждаются в #8.5 и
#8.10 соответственно.
сложный_спецификатор_типа:
ключ typedef-имя
ключ идентификатор
ключ:
class
struct
union
enum
Сложный спецификатор типа можно использовать для ссылки
на имя класса или перечисления там, где имя может быть скрыто
локальным именем. Например:
class x (* ... *);
void f(int x)
(*
class x a;
// ...
*)
Если имя класса или перечисления ранее описано не было,
сложный_спецификатор_типа работает как описание_имени, см.
#8.8.
8.3 Описатели
Список_описателей, появляющийся в описании, есть разде-
ленная запятыми последовательность описателей, каждый из ко-
торых может иметь инициализатор.
список_описателей:
иниц_описатель
иниц_описатель , список_описателей
иниц_описатель:
описатель инициализатор opt
Инициализаторы обсуждаются в #8.6. Спецификатор в описа-
нии указывает тип и класс памяти объектов, к которым
относятся описатели. Описатели имеют синтаксис:
& const opt описатель
описатель ( список_описаний_параметров )
описатель [ константное_выражение opt ]
оп-имя:
простое_оп_имя
typedef-имя :: простое_оп_имя
простое_оп_имя:
идентификатор
typedef-имя
~ typedef-имя
имя_функции_операции
имя_функции_преобразования
Группировка та же, что и в выражениях.
8.4 Смысл описателей
содержит ровно одно оп_имя; оно опреде-
ляет описываемый идентификатор. За исключением описаний неко-
торых специальных функций (см. #8.5.2) , оп_имя будет простым
идентификатором.
Если в качестве описателя возникает ничем не снабженный
идентификатор, то он имеет тип, указанный спецификатором,
возглавляющим описание.
Описатель в скобках эквивалентен описателю без скобок,
но связку сложных описателей скобки могут изменять.
Теперь представим себе описание
t d1
идентификатор (так что тип x в "int x"
есть просто int). Тогда, если d1 имеет вид
*d
то тип содержащегося идентификатора есть "...указатель на t."
Если d1 имеет вид
* const d
то тип содержащегося идентификатора есть "... констант-
ный указатель на t", то есть, того же типа, что и *d, но не
lvalue.
Если d1 имеет вид
&d
или
& const d
&).
Если d1 имеет вид
d (список_описаний_параметров)
то содержащийся идентификатор имеет тип "... функция,
принимающая параметр типа список_описаний_параметров и возв-
ращающая t."
список_описаний_параметров:
список_описаний_парам opt ... opt
список_описаний_парам:
список_описаний_парам , описание_параметра
описание_параметра
= выражение
не получает ни одного параметра. Все описа-
ния для функции должны согласовываться и в типе возвращаемого
значения, а также в числе и типе параметров.
Список_описаний_параметров используется для проверки и
преобразования фактических параметров и для контроля присваи-
вания указателю на функцию. Если в описании параметра указано
выражение, то это выражение используется как параметр по
умолчанию. Параметры по умолчанию будут использоваться в вы-
зовах, где опущены стоящие в хвосте параметры. Параметр по
умолчанию не может переопределяться более поздними описания-
ми. Однако, описание может добавлять параметры по умолчанию,
не заданные в предыдущих описаниях.
По желанию можно задать идентификатор как имя параметра.
Если он присутствует в описании функции, его использовать
нельзя, поскольку он сразу выходит из области видимости. Если
он присутствует в определении функции (#10), то он именует
фармальный параметр.
Если d1 имеет вид
d[ константное_выражение ]
или
d[]
то тип содержащегося идентификатора есть "... массив
объектов типа t". В первом случае константное выражение есть
выражение, значение которого может быть определено во время
компиляции, и тип которого int. (Константные выражения опре-
делены в #12.) Если подряд идут несколько спецификаций "мас-
сив из", то создается многомерный массив; константное выраже-
ние, определяющее границы массива, может быть опущено только
для первого члена последовательности. Этот пропуск полезен,
когда массив является внешним, и настоящее определение, кото-
рое резервирует память, находится в другом месте. Первое
константное выражение может также быть опущено, когда за опи-
сателем следует инициализация. В этом случае используется
размер, вычисленный исходя из числа начальных элементов.
Массив может быть построен из одного из основных типов,
из указателей, из структуры или объединения или из другого
массива (для получения многомерного массива).
не существует массивов функций, хотя
могут быть массивы указателей на функции.
8.4.1 Примеры
Описание
int i; int *pi; int f (); int *fpi (); int (*pif) ();
описывает целое i, указатель pi на целое, функцию f,
возвращающую целое, функцию fpi , возвращающую указатель на
целое, и указатель pif на функцию, возвращающую целое. Осо-
бенно полезно сравнить последние две. Цепочка *fpi() есть
*(fpi()), как предполагается в описании, и та же конструкция
требуется в выражении, вызов функции fpi, и затем использова-
ние косвенного обращения через (указательный) результ, чтобы
получить целое. В описателе (*pif)() дополнительные скобки
необходимы для указания того, что косвенность через указатель
на функцию дает функцию, которая затем вызывается. Функции f
и fpi описаны как не получающие параметров, а pif как указы-
вающая на функцию, не получающую параметров.
Описание
const a = 10, *pc = &a, *const cpc = pc;
int b, *const cp = &b;
описывает a: целую константу, pc: указатель на целую
константу, cpc: константный указатель на целую константу, b:
целое и cp: константный указатель на целое. Значения a, cpc и
cp не могут быть изменены после инициализации. Значение pc
может быть изменено, как и объект, указываемый cp. Примеры
недопустимых выражений:
a = 1;
a++;
*pc = 2;
cp = &a;
cpc++;
Примеры допустимых выражений:
b = a;
*cp = a;
pc++;
pc = cpc;
Описание
fseek (file*,long,int);
описывает функцию, получающую три параметра указанных
типов. Поскольку тип возвращаемого значения не задан, он при-
нимается int (#8.2). Описание
point (int = 0,int = 0);
описывает функцию, которая может вызываться без парамет-
ров, с одним или с двумя параметрами типа int. Ее можно вызы-
вать одним из следующих способов:
point (1,2);
point (1); point ();
Описание
printf (char* ... );
описывает функцию, которая может вызываться с различными
числом и типами параметров. Например
printf ("hello, world");
printf ("a=%d b=%d",a,b);
Однако, всегда ее первым параметром должен быть char*.
Описание
float fa[17], *afp[17];
описывает массив чисел с плавающей точкой и массив ука-
зателей на числа с плавающей точкой. И, наконец,
static int x3d[3][5][7];
является массивом из семи целых. Появ-
ление каждое из выражений x3d, x3d[i], x3d[i][j],
x3d[i][j][k] может быть приемлемо в выражении.
8.4.2 Массивы, Указатели и Индексирование
индексирования [] интерпретируется таким
образом, что e1[e2] идентично *((e1)+(e2)). В силу правил
преобразования, применяемых к +, если e1 массив и e2 целое,
то e1[e2] отностится к e2-ому члену e1. Поэтому, несмотря на
такое проявление асимметрии, индексирование является коммута-
тивной операцией.
массив ранга j*...*k. Если к этому ука-
зателю, явно или неявно, как результат индексирования, приме-
няется операция *, ее результатом является (n-1)-мерный
массив, на который указывалось, который сам тут же преобразу-
ется в указатель.
Рассмотрим, например,
int x[3][5];
x сначала преобразуется, как было сказано, в
указатель, затем i преобразуется к типу x, что включает в се-
бя умножение i на длину объекта, на который указывает указа-
тель, а именно объект из 5 целых. Результаты складываются, и
используется косвенная адресация для получения массива (из 5
целых), который в свою очередь преобразуется в указатель на
первое из целых. Если есть еще один индекс, снова использует-
ся тот же параметр; на этот раз результатом является целое.
массивом, но не играет никакой дру-
гой роли в вычислениях индекса.
8.5 Описания Классов
Класс есть тип. Его имя становится typedef-имя (см.
#8.8), которое может быть использовано даже внутри самого
спецификатора класса. Объекты класса состоят из последова-
тельности членов.
спецификатор_класса:
заголовок_класса (* список_членов opt *)
заголовок_класса (* список_членов opt public : спи-
сок_членов opt *)
заголовок_класса:
сост идентификатор opt
сост идентификатор opt : public opt typedef-имя
сост:
class
struct
union
Объекты классов могут присваиваться, передаваться как
параметры и возвращаться функциями (за исключением объектов
некоторых производных типов, см. #8.5.3). Прочие действия,
которые могут быть удобны, может определить пользователь, см.
#8.5.11.
Структура является классом, все члены которого общие,
см. #8.5.9. Объединение является структурой, содержащей в
каждый момент только один член, см. #8.5.13. Список_членов
может описывать друзей (8.5.10) и члены вида: данные, функ-
ция, класс, перечисление, поле(#8.5.13). Список_членов может
также содержать описания, регулирующие видимость имен членов,
см. #8.5.9.
идентификатор opt : константное_выражение
Члены, являющиеся классовыми объектами, должны быть объ-
ектами предварительно описанных классов. В частности, класс
cl не может содержать объект класса cl, но он может содержать
указатель на объект класса cl.
Вот простой пример описания структуры:
struct tnode
(*
char tword[20];
int count;
tnode *left;
tnode *right;
*);
содержащей массив из 20 символов, целое и два указателя
на такие же структуры. Если было дано такое описание, то опи-
сание
tnode s, *sp
описывает s как структуру данного сорта и sp как указатель на
структуру данного сорта. При наличии этих описаний выражение
sp->count
ссылается на поле count структуры, на которую указывает sp;
s.left
ссылается на указатель левого поддерва структуры s; а
s.right->tword[0]
ссылается на первый символ члена tword правого поддерва стру-
ктуры s.
8.5.1 Статические Члены
в программе. На статический член mem
класса cl можно ссылаться cl:mem, то есть без ссылки на объ-
ект. Он существует, даже если не было создано ни одного объ-
екта класса cl. Для статического члена не может задаваться
никакой инициализатор, и он не может быть членом класса с
конструктором.
8.5.2 Функции Члены
Функция, описанная как член, (без спецификатора friend
(#8.5.10)) называется функцией членом и вызывается с исполь-
зованием синтаксиса члена класса (#7.1). Например:
struct tnode
(*
char tword[20];
int count;
tnode *left;
tnode *right;
void set (char* w,tnode* l,tnode* r);
*);
tnode n1, n2;
n1.set ("asdf",&n2,0);
n2.set ("ghjk",0,0);
Определение функции члена рассматривается как находящее-
ся в области видимости ее класса. Это значит, что она может
непосредственно использовать имена ее класса. Если определе-
ние функции члена лексически находится вне описания класса,
то имя функции члена должно быть уточнено именем класса с по-
мощью операции ::. Определения функций обсуждаются в #10.
Например:
void tnode.set (char* w,tnode* l,tnode* r) (*
count = strlen (w);
if (sizeof (tword)<=count)
error ("tnode string too long");
strcpy (tword,w);
left = l;
right = r;
*)
вызвана функция. Так, в вызове
n1.set("abc",0,0) tword ссылается на n1.tword, а в вызове n2.
set("def",0,0) оно ссылается на n2.tword. Предполагается, что
функции strlen, error и strcpy описаны где-то в другом месте,
см. #10.
В функции члене ключевое слово this является указателем
на объект, для которого вызвана функция.
Функция член может быть определена (#10) в описании
класса, и в этом случак она является inline (#8.1). Например:
int b;
struct x
(*
int f () (* return b; *)
int f () (* return b; *)
int b;
*);
означает
int b;
struct x
(*
int f ();
int b;
*);
inline x::f () (* return b; *)
Применение операции получения адреса к функциям членам
допустимо. Однако, тип параметра результирующего указателя на
функцию неопределн, поэтому любое использование его является
зависимым от реализации.
8.5.3 Производные Классы
В конструкции
сост идентификатор : public opt typedef-имя
typedef-имя должно означать ранее описанный класс, назы-
ваемый базовым классом для описываемого класса. Говорится,
что последний выводится из предшествующего (является произ-
водным от него). По поводу смысла public см. #8.5.9. На члены
базового класса можно ссылаться так, как если бы они были
членами производного класса, за исключением тех случаев, ког-
да имя базового члена было переопределено в производном клас-
се; в этом случае для ссылки на скрытое имя можно использо-
вать операцию :: (#7.1). Производный класс сам может
использоваться в качестве базового класса. Невозможно стро-
исть производные от union (#8.5.13). Указатель на производный
класс может неявно преобразовываться в указатель на открытый
базовый класс (#6.7).
Для объектов класса, производного от класса, для которо-
го была определена operator= (#8.5.11), присваивание неявно
не определено (#7.14 и #8.5)
Например:
class base
(*
int a, b;
*);
class derived : public base
(*
int b, c;
*);
derived d;
d.a = 1;
d.base::b = 2;
d.b = 3;
d.c = 4;
осуществляет присваивание четырем членам d.
8.5.4 Виртуальные Функции
класса derived вызывает derived::vf.
Например:
struct base
(*
virtual void vf ();
void f ();
*);
class derived : public base
(*
void vf ();
void f ();
*);
derived d;
base* bp = &d;
bp->vf();
bp->f();
Вызовы вызывают, соответственно, derived::vf и base::f
для объекта класса derived, именованного d. Так что интерпре-
тация вызова виртуальной функции зависит от типа объекта, для
которого она вызвана, в то время как интерпретация вызова не-
виртуальной функции зависит только от типа указателя, обозна-
чающего объект.
Виртуальная функция не может быть другом (friend) (#8.5.
10). Функция f в классе, выведенном из класса, который имеет
виртуальную функцию f, сама считается виртуальной. Виртуаль-
ная функция в базовом классе должна быть определена. Вирту-
альная функция, которая была определена в базовом классе, не
обязательно должна определяться в производном классе. В этом
случае во всех вызовах используется функция, определенная для
базового класса.
8.5.5 Конструкторы
Функция член с именем, совпадающим с именем ее класса,
называется конструктором. Если класс имеет конструктор, то он
вызывается для каждого объекта этого класса перед тем, как
этот объект будет как- либо использован, см. #8.6.
Конструктор не может быть virtual или friend.
Если класс имеет базовый класс или объекты члены с конс-
трукторами, их конструкторы вызываются до конструктора произ-
водного класса. Первым вызывается конструктор базового клас-
са. Объяснение того, как для таких конструктороу могут
специфицироваться параметры , см. в #10, а того, как конс-
трукторы могут использоваться для управления свободной па-
мятью, см. в #8.5.8.
Объект класса с конструктором не может быть членом объ-
единения.
Для конструктора нельзя задать возвращаемое значение,
как нельзя использовать оператор return в теле конструктора.
Конструктор может явно применяться для создания новых
объектов его типа используя синтаксис
typedef-имя ( список_параметров opt )
Например,
complex zz = complex (1,2.3);
cprint (complex (7.8,1.2));
Объекты, созданные таким образом, не имеют имени (если
только конструктор не использован как инициализатор, как это
было с zz выше), и их время жизни ограничено областью види-
мости, в которой они созданы.
8.5.6 Преобразования
Конструктор, получающий один параметр, определяет преоб-
разование из типа своего параметра в тип своего класса. Такие
преобразования неявно применяются дополнительно к стандартным
пробразованиям (#6.6-7). Поэтому присваивание объекту из
класса x допустимо, если тип t присваиваемого значения есть
x, или если было описано преобразование из t в x. Аналогично
конструкторы используются для преобразования инициализаторов
(#8.6), параметров функции (#7.1) и возвращаемых функцией
значений (#9.10). Например:
class x (* ... x (int); *);
f (x arg) (*
x a = 1; // a = x (1)
a = 2; // a = x (2)
f (3); // f (x (3))
*)
Если ни один конструктор для класса x не получает прис-
ваиваемый тип, то не делается никаких попыток отыскать другие
конструкторы для преобразования присваиваемого значения в
тип, который мог бы быть приемлем для конструкторов класса x.
Например:
class x (* ... x (int); *);
class x (* ... y (x); *);
y a = 1; // недопустимо: y (x (1)) не пробуется
Функция член класса x с именем вида
имя_функции_преобразования:
operator тип
#8.9), или его можно вызвать явно с
помощью записи приведения к типу. Например:
class x (*
// ...
operator int();
*);
x a;
int i = int(a);
i = (int)a;
i = a;
Во всех трех случаях значене будет преобразовываться
функцией x::operator int(). Применение определяемых пользова-
телем преобразований не сводится только к присваиваниям и
инициализациям. Например:
x a, b;
// ...
int i = (a) ? 1+a : 0;
int j = (a&&b) ? a+b : i;
8.5.7 Деструкторы
Функция член класса cl с именем ~cl называется деструк-
тором. Деструктор не возвращает никакого значения и не полу-
чает никаких параметров; он используется для уничтожения зна-
чений типа cl непосредственно перед уничтожением содержащего
их объекта. Деструктор не может быть вызван явно.
Деструктор для базового класса выполняется после дест-
руктора производного от него класса. Деструкторы для объектов
членов выполняются после деструктора для объекта, членом ко-
торого они являются. Как деструкторы используютя для управле-
ния свободной памятью, см. объяснение в #8.5.8.
Объект класса с деструктором не может быть членом объ-
единения.
8.5.8 Свободная Память
Когда с помощью операции new создается классовый объект,
то для получения необходимой памяти конструктор будет (неяв-
но) использовать operator new (#7.1). Конструктор может осу-
ществить свое собственное резервирование памяти посредством
присваивания указателю this до каких-либо использований чле-
на. С помощью присваивания this значения ноль деструктор мо-
жет избежать стандартной операции дерезервирования памяти для
объекта его класса. Например:
class cl (*
int v[10];
cl () (* this = my_own_allocator (sizeof (cl)); *)
~cl () (* my_own_deallocator (this); this = 0; *)
*)
На входе в конструктор this являеется не-нулем, если ре-
зервирование памяти уже имело место (как это имеет место для
auto, static объектов и объектов членов), и нулем в остальных
случаях.
Вызовы конструкторов для базового класса и объектов чле-
нов будут иметь место после присваивания указателю this. Если
конструктор базового класса осуществляет присваивание this,
то новое значение также будет использоваться конструктором
производного класса (если таковой есть).
При уничтожении вектора объектов класса с деструктором
необходимо указывать число элементов. Например:
class x (* ... ~x(); *);
x* p = new x [size];
delete[size] p;
8.5.9 Видимость Имен Членов
метки "public:". В этом случае они являются
открытыми. Открытый член может использоваться любой функцией.
struct является классом, все члены которого общие, см.
#8.5.12.
Если производный класс описан как struct или если перед
именем базового класса в описании производного класса стоит
ключевое слово public, то общие члены базового класса являют-
ся общими для производного класа; в остальных случаях они яв-
ляются закрытыми. Открытый член mem закрытого базового класса
base может быть описан как общий для производного класса с
помощью опиисания вида
typedef-имя :: идентификатор ;
в котором typedef-имя обозначает базовый класс, а иден-
тификатор есть имя члена базового класса. Такое описание
должно стоять в открытой части производного класса. Рассмот-
рим
class base (*
int a;
public:
int b, c;
int bf();
*);
class derived : base (*
int d;
public:
base::c;
int e;
int df();
*);
int ef(derived&);
функция bf может использовать члены a, b,
c и bf.
8.5.10 Друзья
Друг класса - это функция не-член, которая может исполь-
зовать имена закрытых членов. Друг не принадлежит области ви-
димости класса и не вызывается с помощью синтаксиса выбора
члена (если он не является членом другого класса). Следующий
пример иллюстрирует различия между членами и друзьями:
class private (*
int a;
friend void friend_set(private*, int);
public:
void member_set(int);
*);
void friend_set (private* p, int i) (* p->a = i; *)
void private::member_set (int i) (* a = i; *)
private obj;
friend_set (&obj,10);
obj.member_set (10);
Если описание friend отностися к перегруженному имени
или операции, то другом становится только функция, задаваемая
типами параметров. Член класса cl1 может быть другом класса
cl2. Например:
class cl2 (*
friend char* cl1::foo(int);
// ...
*);
Все функции класса cl1 могут быть сделаны друзьями клас-
са cl2 с помощью одного описания
class cl2 (*
friend class cl1 ;
// ...
*);
Функция член, определенная (#10) в описании класса, яв-
ляется inline.
8.5.11 Функция Операция
Большинство операций могут быть перегружены с тем, чтобы
они могли получать в качестве операндов объекты класса.
имя_функции_операции:
operator операция
операция: одна из
new delete
+ - * / % ^ & ! ~
! = < > += -= *= /= %=
^= &= != << >> <<= >>= == !=
<= >= && !! ++ -- () []
Последние две операции - это вызов функции и индексиро-
вание. Функция операция (за исключением operator new и
operator delete; см. #7.2) должна быть или функцией членом,
или получать по меньшей мере один классовый параметр. См.
также #7.16.
8.5.12 Структуры
Структура есть класс, все члены которого общие. Это зна-
чит, что
struct s (* ... *);
эквивалентно
class s (* public: ... *);
Структура может иметь функции члены (включая конструкто-
ры и деструкторы). Базовй класс производной struct является
открытым. Это значит, что
struct s : d (* ... *);
эквиволентно
class s : public b (* public: ... *);
8.5.13 Объединения
может храниться не больше одного из объ-
ектов членов. Объединение может иметь функции члены (включая
конструкторы и деструкторы). Из объединения невозможно вывес-
ти класс. Объект класса с конструктором или деструктором не
может быть членом объединения.
Объединение вида
union (* список_членов *);
называется безымянным объединением; оно определяет неи-
менованный объект. Имена членов безымянного объединения долж-
ны отличаться от других имен в области видимости, в которой
объединение описано; в этой области видимости они могут ис-
пользоваться непосредственно, без обычного синтаксиса доступа
к членам (#8.5). Например:
union (* int a; char* p; *);
a = 1;
// ...
p = "asdf";
Здесь a и p используются как простые переменные (не-чле-
ны), но так как они являются членами объединения, они имеют
один и тот же адрес.
8.5.14 Поля Бит
Описатель_члена вида
идентификатор opt : константное_выражение
определяет поле; его длина отделяется от имени поля дво-
еточием. Поля упаковываются в машинные целые; они не являются
альтернативой слов. Поле , не влезающее в оставшееся в целом
место, помещается в следующее слово. Поле не может быть шире
слова. На некоторых машинах они размещаются справа налево, а
на некоторых слева направо, см. #2.6.
Неименованные поля полезны при заполнении для согласова-
ния внешне предписанных размещений (форматов). В особых слу-
чаях неименованные поля длины 0 задают выравнивание следующе-
го поля по границе слова. Не требуется аппаратной поддержки
любых полей, кроме целых. Более того, даже целые поля могут
рассматриваться как unsigned. По этим причинам рекомендуется
описывать поля как unsigned. К полям не может применяться
операция получения адреса &, поэтому нет указателей на поля.
Поля не могут быть членами объединения.
8.5.15 Вложенные Классы
Класс может быть описан внутри другого класса. Это, од-
нако, лишь соглашение о записи, поскольку внутренний класс
принадлежит охватывающей области видимости. Например:
int x;
class enclose (* // охватывающий
int x;
class inner (* // внутренний
int y;
void f(int);
*);
int g(inner*);
*);
inner a;
void inner::f(int i) (* x = i; *) // присваивает ::x
int enclose::g(inner* p) (* return p->y; *) // ошибка
8.6 Инициализация
Описатель может задавать начальное значение описываемого
идентификатора.
(* список_инициализаторов *)
Все выражения в инициализаторе статической или внешней
переменной должны быть константными выражениями, которые опи-
саны в #12, или выражениями, которые сводятся к адресам ранее
описанных переменных, возможно со смещением на константное
выражение. Автоматические и регистровые переменные могут ини-
циализироваться любыми выражениями, включащими константы, ра-
нее описанные переменные и функции.
Гарантируется, что неинициализированные статические и
внешние переменные получают в качестве начального значения 0.
Гарантируется, что неинициализированные автоматические и ре-
гистровые переменные получают в качестве начального значения
"пустое место"*.
--------------------
* В английском - "garbage", означающее затертое место
[памяти], т.е. если переменная целая, то 0, если char, то
'\0', если указатель на Т, то (Т*) null. (прим. перев.)
Когда инициализатор применяется к скаляру (указатель или
объект арифметического типа), он состоит из одного выражения,
возможно, заключенного в фигурные скобки. Начальное значение
объекта находится из выражения; выполняются те же преобразо-
вания, что и при присваивании.
Заметьте, что поскольку () не является инициализатором,
то x a(); является не описанием объекта класса x, а описанием
функции, не получающей значений и возвращающей x.
8.6.1 Список Инициализаторов
Когда описанная переменная является составной (класс или
массив), то инициализатор может состоять из заключенного в
фигурные скобки, разделенного запятыми списка инициализаторов
для членов составного объекта, в порядке возрастания индекса
или по порядку членов. Если массив содерхит составные подобъ-
екты, то это правило рекурсивно применяется к членам состав-
ного подобъекта. Если инициализаторов в списке меньше, чем
членов в составном подобъекте, то составной подобъект допол-
няется нулями.
Фигурные скобки могут опускаться следующим образом. Если
инициализатор начинается с левой фигурной скобки, то следую-
щий за ней список инициализаторов инициализирует члены сос-
тавного объекта; наличие числа инициализаторов, большего, чем
число членов, считается ошибочным. Если, однако, инициализа-
тор не начинается с левой фигурной скобки, то из списка бе-
рутся только элементы, достаточные для сопоставления элемен-
там составного объекта; все остающиеся элементы оставляются
для инициализации следующего элемента составного объекта,
частью которого является текущий составной объект.
Например,
int x[] = (* 1, 3, 5 *);
описывает и инициализирует x как одномерный массив, име-
ющий три элемента, поскольку размер не был указан и дано три
инициализатора.
float y[4][3] = (*
(* 1, 3, 5 *),
(* 2, 4, 6 *),
(* 3, 5, 7 *)
*);
является полностью снабженной квадратными скобками ини-
циализацией: 1,3 и 5 инициализируют первый ряд массива y[0],
а именно, y[0][0], y[0][1] и y[0][2]. Аналогично, следующие
две строки инициализируют y[1] и y[2]. Инициализатор заканчи-
вается раньше, поэтому y[3] инициализируется 0-ями. В точ-
ноcти тот же эффект может быть достигнут с помощью
float y[4][3] = (*
1, 3, 5, 2, 4, 6, 3, 5, 7
*);
Инициализатор для y начинается с левой фигурной скобки,
но не начинается с нее инициализатор для y[0], поэтому ис-
пользуется три значения из списка. Аналогично, следующие три
успешно используются для y[1] и следующие три для y[2]. Так
же
float y[4][3] = (* (* 1 *), (* 2 *), (* 3 *), (* 4 *) *);
инициализирует первый столбец y (рассматриваемого как
двумерный массив) и оставляет остальные элементы нулями.
8.6.2 Объекты Классов
Объект с закрытыми членами не может быть инициализован
списком инициализаторов; это же относится к объекту объедине-
ние. Объект класса с конструктором должен инициализироваться.
Если класс имеет конструктор, не получающий параметров, то
этот конструктор используется для объектов, которые явно не
инициализированы. Список параметров для конструктора можно
добавлять к имени в описании или к типу в выражении new. Сле-
дующие инициализации все дают одно и тоже значение (#8.4):
struct complex (*
float re;
float im;
complex (float r,float i = 0) (* re=r; im=i; *)
*);
complex zz1(1,0);
complex zz2(1);
complex* zp1 = new complex (1,0);
complex* zp1 = new complex (1);
Объекты класса могут также инициализироваться с помощью
явного использования операции =. Например:
complex zz3 = complex (1,0);
complex zz4 = complex (1);
complex zz5 = 1;
complex zz6 = zz3;
Если есть конструктор, получающий ссылку на объект свое-
го собственного класса, то он будет вызываться при инициали-
зации объекта другим объектом этого класса, но не при инициа-
лизации объекта конструктором.
Объект может быть членом составного объекта только (1)
если класс объекта не имеет конструктора, или (2) если его
конструкторы не имеют параметров, или (3) если составной объ-
ект является классом с конструктором, который задает список
инициализации члена (см. #10). В случае 2 конструктор вызыва-
ется при создании составного объекта. Если составной объект
является классом (но не тогда, когда он является вектором)
для вызова конструктора могут использоваться параметры по
умолчанию. Если член составного объекта является членом клас-
са с деструкторами, то этот деструктор вызывается при уничто-
жении составного объекта.
Конструкторы для нелокальных статических объектов вызы-
ваются в порядке их появления в файле; деструкторы для
вызываются в обратном порядке. Вызывается ли конструктор или
деструктор для локального статического объекта в случае если
функция, в которой объект описан, не вызывается, не определе-
но. Если конструктор для локального статического объекта вы-
зывается, то он вызывается после конструкторов для глобальных
объектов, лексически ему предшествующих. Если для локального
статического объекта вызывается деструктор, то он вызывается
до деструкторов для глобальных объектов, лексически ему пред-
шествующих.
8.6.3 Ссылки
Когда переменная описана как t&, то есть "ссылка на тип
t", она должна быть инициализирована или объектом типа t, или
объектом объектом, который может быть преобразован в t. Ссыл-
ка становится другим именем объекта. Например:
int i;
int& r = i;
r = 1; // значение i становится 1
int* p = &r; // p указывает на i
Значение ссылки не может быть изменено после инициализа-
ции. Заметьте, что обработка инициализации ссылки очень силь-
но зависит от того, что ей присваивается.
Если инициализатор для ссылки на тип t не является
lvalue, то будет создан и инициализован инициализатором объ-
ект типа t. Тогда ссылка станет именем для этого объекта.
Время жизни объекта, созданного таким способом, будет область
видимости, в которой он создан. Например:
double& rr = 1;
допустимо, и rr будет указывать на объект типа double,
содержащий значение 1.0.
Заметьте, что ссылка на класс b может быть инициализиро-
вана объектом класса d при условии, что b является открытым
базовым классом класса d (в этом случае d есть b).
Ссылки особенно полезны в качестве типов параметров.
Например:
struct b (* ... *);
struct d : b (* ... *);
int f(b&);
d a;
f(a);
8.6.4 Массивы Символов
Массив char можно инициализировать строкой. Последова-
тельные символы строки инициализируют члены массива. Напри-
мер:
char msg[] = "syntax error on line %d\n";
демонстрирует массив символов, члены которого инициали-
зированы строкой. Обратите внимание, что sizeof(msg)==25.
8.7 Имена Типов
для объекта этого типа, в котором
опущено имя объекта.
имя_типа:
спецификатор_типа абстрактный_описатель
)
Возможно единственным образом идентифицировать положение
в абстрактном_описателе, где должен стоять идентификатор в
случае, если бы конструкция была описателем в описании. Тогда
именованный тип является - тот же, что и тип гипотетического
идентификатора. Например,
int
int *
int *[3]
int (*)[3]
int *()
int (*)()
именуют, соответсвенно, типы "целое", "указатель на це-
лое", "массив из 3 указателей на целые", "указатель на массив
из 3 целых", "функция, возвращающая указатель на целое" и
"указатель на функцию, возвращающую целое".
8.8 typedef - Определение Типа
Описания, содержащие спецификатор_описания typedef, оп-
ределяют идентификаторы, которы позднее могут использоваться
так, как если бы они были ключевыми словами, именующими ос-
новные или производные типы.
typedef-имя:
идентификатор
Внутри области видимости описания, содержащего typedef,
каждый идентификатор, возникающий как часть какого-либо опи-
сателя, становится в этом месте синтаксически эквивалентным
ключевому слову типа, которое именует тип, ассоциированный с
идентификатором таким обрахом, как описывается в #8.4. Специ-
фикатор_описания typedef не может использоваться для члена
класса. Имя класса или перечисления также является
typedef-именем. Например, после
typedef int miles, *klicksp;
struct complex (* double re, im; *);
каждая из конструкций
miles distance;
extern klicksp metricp;
complex z, *zp;
является допустимым описанием; distance имеет тип int,
metricp имеет тип "указатель на int".
typedef вводит не новые типы, но только синонимы для ти-
пов, которые могли бы быть определены другим путем. Так в
приведенном выше примере distance рассматривается как имеющая
в точности тот же тип, что и любой другой int объект.
Но описание класса вводит новый тип. Например:
struct x (* int a; *);
struct y (* int a; *);
x a1;
y a2;
int a3;
описывает три переменных трех различных типов.
Описание вида
описание_имени:
сост идентификатор ;
enum идентификатор ;
специфицирует, что идентификатор является именем некото-
рого (возможно, еще не определенного) класса или перечисле-
ния. Такие описания позволяют описывать классы, ссылающихся
друг на друга. Например:
class vector;
class matrix (*
// ...
friend vector operator*(matrix&, vector&);
*);
class vector (*
// ...
friend matrix operator*(matrix&, vector&);
*);
8.9 Перегруженные Имена Функций
В тех случаях, когда для одного имени определено нес-
колько (различных) описаний функций, это имя называется пе-
регруженным. При использовании этого имени правильная функция
выбирается с помощью сравнения типов фактических параметров с
типами формальных параметров в описаниях функций.
Поиск того, какую функцию вызвать, осуществляется в три
отдельных шага:
Искать точно соответствующую и использовать, если найде-
на.
Искать соответствующую с использованием стандартных пре-
образований (#6.6-8) и использовать любую найденную.
Искать соответствующую с использованием определенных
пользователем преобразований (#6.5.6). Если найдено единс-
твенное множество преобразований, использовать ее.
Ноль, char или short считаются точно соответствующими
формальному параметру типа int. float считаются точно соот-
ветствующими формальному параметру типа double.
Над параметром перегруженной функции выполняются только
следующе преобразования: int в long, int в double и преобра-
зования указателей и ссылок (#6.7-8).
Для того, чтобы перегрузить имя функции не члена и не
функции operator, любому описанию функции должно предшество-
вать описание overload, см. #8.1. Например:
overload abs;
double abs(double);
int abs(int);
abs(1); // вызывается abs(int);
abs(1.0); // вызывается abs(double);
Например:
class x (* ... x (int); *);
class y (* ... y (int); *);
class z (* ... z (char*); *);
overload int f (x), f (y);
overload int g (x), g (z);
f (1); // недопустимо: f(x(1)) или f(y(1))
g (1); // g(x(1))
g ("asdf"); // g(z("asdf"))
& может применяться к перегружен-
ному имени только в присваивании или инициализации, когда
ожидаемый тип определяет, адрес какой функции брать. Напри-
мер:
&, matrix&);
int operator=(vector&, vector&);
int (*pfm)(matrix&, matrix&) = &operator=;
int (*pfv)(vector&, vector&) = &operator=;
int (*pfx)(...) = &operator=;
8.10 Описания Перечислений
Перечисления являются типами int с именованными констан-
тами.
enum_спецификатор:
enum идентификатор opt (* enum_список *)
enum_список:
перечислитель
enum_список , перечислитель
перечислитель:
идентификатор
идентификатор = константное_выражение
Идентификаторы в enum-списке описаны как константы и мо-
гут появляться во всех местах, где требуются константы. Если
не появляется ни одного перечислителя с =, то значения соот-
ветствующих констант начинаются с 0 и возрастают на 1 по мере
чтения описания слева нарпаво. Перечислитель с = дает ассоци-
ированному с ним идентификатору указанное значение; последую-
щие идентификаторы продолжают прогрессию от присвоенного зна-
чения.
Имена перечислителей должны быть отличными от имен
обычных переменных. Имена перечислителей с разными константа-
ми тоже должны быть различны. Значения перечислителей не обя-
зательно должны быть различными.
Роль идентификатора в спецификаторе перечисления
enum_спецификатор полностью аналогична роли имени класса; он
именует определенный нутератор. Например:
enum color (* red, yellow, green=20, blue *);
color col = red;
color *cp = &col;
if (*cp == blue) // ...
21 *).
8.11 Описание asm
Описание asm имеет вид
asm ( строка );
Смысл описания asm неопределен. Обычно оно используется
для передачи информации ассемблеру через компилятор.
[Назад] [Содержание] [Вперед]
| Главная |