Справочное руководство по C++ - Бьярн Страустрап
Шрифт:
Интервал:
Закладка:
float y[4][3] = {
{1}, {2}, {3}, {4}
};
инициализируется первый столбец y (который рассматривается как двумерный массив), а остальные столбцы принимают значение 0.
Инициализация массива объектов типа класс с помощью конструкторов описывается в §R.12.6.1.
Инициализатор для объединения без конструктора должен быть или отдельным выражением типа объединения, или заключенным в фигурные скобки, инициализатором первого члена объединения, например,
union u {i nt a; char* b; };
u a = {1};
u b = a;
u c = 1; // ошибка
u d = {0, "asdf"}; // ошибка
u e = {"asdf"}; // ошибка
Число инициализаторов не должно превышать числа членов или элементов, которые инициализируются. Например, следующая инициализация ошибочна:
char cv[4] = {'a', 's', 'd', 'f', 0}; // ошибка
R.8.4.2 Символьные массивы
Массив символов (неважно, знаковых или беззнаковых) можно инициализировать строкой-литералом: символы строки последовательно инициализируют элементы массива. Следующее определение дает пример символьного массива, элементы которого инициализируются строкой:
char msg[] = "Syntax error on line %sn";
Заметим, что поскольку 'n' задает один символ, и поскольку добавляется завершающий символ ' ', sizeof(msg) равно 25.
Нельзя задавать больше инициализаторов, чем есть элементов в массиве, поэтому следующий пример ошибочен: здесь нет места для подразумевающегося символа конца строки (' '):
char cv[4] = "asdf"; // ошибка
R.8.4.3 Ссылки
Переменная, описанная как T&, т.е. "ссылка на тип T" (§R.8.2.2), должна инициализироваться объектом типа T или объектом, который можно преобразовать к типу T, например,
void f()
{
int i;
int& r = i; // `r' ссылается на `i'
r = 1; // `i' принимает значение 1
int* p = &r; // `p' указывает на `i'
int& rr = r; // `rr' ссылается на то, на что ссылалось `r',
// т.е. на `i'
};
Ссылку после инициализации нельзя изменять так, чтобы она обозначала другой объект. Отметим, что инициализация ссылки трактуется совсем не так, как присваивание ссылке. Передача параметра (§R.5.2.2) и операция возврата значения функции (§R.6.6.3) считаются инициализацией.
Инициализатор для ссылки можно опускать только в описании параметра (§R.8.2.5), в описании возвращаемого функцией типа, в описании члена класса при описании самого класса (§R.9.2) и там, где явно использована спецификация extern, например,
int& r1; // ошибка: нет инициализации
extern int& r2; // нормально
Если инициализатор для ссылки на тип T является адресом типа T или типом, производным от T (§R.10), для которого T служит доступным базовым типом (§R.4.6), ссылка будет обозначать значение, заданное инициализатором. Иначе, в том и только том случае, когда ссылка обозначает объект со спецификацией const, будет создан объект типа T и проинициализирован значением, заданным инициализатором.
Теперь ссылка играет роль имени этого объекта, например,
double d = 1.0;
double& rd = d; // rd ссылается на `d'
const double& rcd = d; // rcd ссылается на `d'
double& rd2 = 1; // ошибка: несоответствие типа
const double& rcd2 = 1; // rcd2 ссылается на временный объект
// со значением `1'
Ссылку на volatile T можно инициализировать объектом типа volatile T или просто T, но не const T. Ссылку на const T можно инициализировать const T, просто T или чем-то, что можно преобразовать в тип T, но не volatile T. Ссылку на тип T (без const или volatile) можно инициализировать только объектом типа T.
Время жизни временного объекта, созданного при описанной инициализации, определяется текущей областью видимости, в которой он был создан (§R.3.5). Отметим, что ссылку на класс B можно инициализировать объектом класса D при условии, что В является однозначно определенным и доступным базовым классом для D (тогда говорят, что "D есть B"), см. §R.4.7.
R.9 классы
Класс есть тип. Его имя используется как имя-класса (§R.9.1), т.е. становится зарезервированным словом в его области видимости.
имя-класса:
идентификатор
Для образования конструкции имя-класса используются спецификации-класса и спецификации-сложного-типа (§R.7.1.6). Объект класса состоит из последовательности (возможно пустой) членов.
спецификация-класса:
заголовок-класса {список-членов opt}
заголовок-класса:
служебное-слово-класса идентификатор opt спец-базовых opt
служебное-слово-класса имя-класса спец-базовых opt
служебное-слово-класса:
class
struct
union
Имя класса можно использовать в качестве конструкции имя-класса даже в списке-членов самого этого класса. Обычно спецификацию-класса называют описанием класса. Класс считается определенным, как только появится спецификация-класса, несмотря на то, что его функции-члены могут быть еще неопределены.
Объекты пустого класса имеют ненулевой размер.
Объекты типа класс можно присваивать, передавать в качестве параметров функций и получать в качестве значения, возвращаемого функцией (за исключением объектов тех классов, для которых копирование ограничено, см. §R.12.8). Другие возможные операции, такие, как сравнение на равенство, могут определяться пользователем, см. §R.13.4.
Структурой называется класс, описанный со служебным-словом-класса struct; ее члены и базовые классы (§R.10) считаются общими по определению (§R.11). Объединением называется класс, описанный со служебным-словом-класса union; его члены считаются общими по определению, и в любой момент времени объединение содержит только один член (§R.9.5).
R.9.1 Имена класса
Описание класса порождает новый тип. Например, ниже описываются три переменные трех различных типов:
struct X { int a; };
struct Y { int a; };
X a1;
Y a2;
int a3;
Отсюда следует, что такие присваивания приводят к несоответствию типов:
a1 = a2; // ошибка: Y присваивается X
a1 = a3; // ошибка: int присваивается X
Ниже описывается перегрузка (§R.13) функции f(), а не просто повторное описание той же функции:
int f(X);
int f(Y);
По той же причине нельзя дважды определять класс, это видно из примера ниже, где дважды определен S:
struct S { int a; };
struct S { int a; }; // ошибка, повторное определение
Описание класса включает имя класса в ту область видимости, внутри которой оно произошло, и закрывает любой класс, объект, функцию или другое описание этого имени в объемлющей области видимости (§R.3.2). Если имя класса описано в такой области видимости, где уже был описан объект с таким же именем, функция или элемент перечисления, то обращаться к классу можно только с помощью конструкции спецификация-сложного-типа (§R.7.1.6), например:
struct stat {
//…
};
stat gstt; // просто `stat' используется для
// определения переменной
int stat(struct stat*); // переопределение `stat' как функции
void f()
{
struct stat* ps; // нужен префикс struct
// для задания структуры stat
//…
stat(ps); // вызов stat()
//…
}
Конструкция спецификация-сложного-типа вместе со служебным-словом-класса, но без описания объекта или функции также может служить для задания имени класса, как и описание класса, однако в этом случае класс не считается определенным, например:
struct s { int a; };
void g()
{
struct s; // скрывает глобальную структуру `s'
s* p; // используется локальная структура `s'
struct s { char* p; }; // описание локальной структуры `s'
}
Такие правила позволяют классам ссылаться друг на друга при их описании, пример,
class vector;
class matrix {
//…
friend vector operator*(matrix&, vector&);
};
class vector {
//…
friend vector operator*(matrix&, vector&);
};
Описание friend (дружественные функции) обсуждается в §R.11.4, а функция operator в §R.13.4. Если класс, указанный как друг, пока еще не описан, его имя считается принадлежащим той же области видимости, в которой находится имя класса, содержащего описание friend (§R.11.4).
В описании объектов или функций можно также использовать конструкцию спецификация-сложного-типа (§R.7.1.6). Ее использование отличается от описания класса тем, что если класс, чье имя указано в спецификации, находится в текущей области видимости, то имя из этой спецификации будет ссылаться на него, например: