С++ обзор языка



страница2/10
Дата22.06.2019
Размер1.54 Mb.
1   2   3   4   5   6   7   8   9   10

3.6.2. Производные типы
Существует потенциально бесконечное число производных типов, сконструированных из основных типов следующими способами:
массивы (arrays) объектов заданного типа (8.2.4);

функции (functions) с параметрами заданных типов, возвращающие значение

указанного типа (8.2.5);



указатели (pointers) на объекты или функции заданного типа (8.2.1);

ссылки (references) на объекты или функции заданного типа (8.2.2);

константы (constants), являющиеся значениями заданного типа (7.1.6);

классы (classes), определяющие объекты, возможно составленные из значений

разных типов (9), набор функций для работы с этими объектами (9.3) и

набор ограничений на доступ к объектам и функциям (11);

структуры (structures), которые являются классами без ограничений на доступ

к объктам и функциям-членам структуры (11);



объединения (unions), являющиеся структурами, способными содержать

объекты разных типов в разные моменты времени (9.5);



указатели на компоненты класса (pointers to class members), которые (8.2.3)

идентифицируют компоненты заданного типа среди объектов данного



класса.
Объекты типа void* (указатель на void), const void* и volatile void* могут указывать на объекты неизвестного типа. Представление типа void* должно содержать достаточное число бит, чтобы хранит указатель на любой объект.
3.6.3. Именование типов
Основные и производные типы могут получать новые имена посредством механизма typedef (7.1.3), а семейство типов или функций может быть задано или поименовано посредством шаблонов (14). Пример:
typedef complex point;
3.7. L-значения
Объект есть область памяти; l-значением (l-value) называется выражение, ссылающееся на объект или функцию. Очевидным примером l-выражения является имя объекта. Некоторые операции требуют l-значение. Например, если Е есть выражение, вырабатывающее указатель, то E* есть l-выражение, ссылающееся на объект, на который указывает Е. Название “l-значение” возникло из оператора присваивания Е1 := Е2, в котором левый операнд должен быть l-значением. Пример сложного l-выражения:

p[a+10] = 7;

4. Стандартные преобразования
Некоторые операции могут в зависимости от своих операндов приводить значение операнда от одного типа к другому.
4.1. Целочисленные расширения
Значения типов char и short int, элементы перечисления, объекты типа перечисления (7.2) и целые битовые поля (9.6) могут употребляться везде, где употребляются целые. Если int может представить все значения исходного типа, значение приводится к int, в противном случае оно преобразуется в unsigned int. Этот процесс называется целочисленным расширением (integral promotion).
4.2. Целочисленные преобразования
Когда целое преобразуется к беззнаковому типу, получатся значение наименьшего целого без знака, конгруэнтного целому со знаком. Когда значение преобразуется к знаковому типу, оно остается неизменным, если оно может быть представлено в новом типе, в противном случае результат зависит от реализации.
4.3. Плавающие типы одинарной и двойной точности
Для выражений типа float используется вещественная арифметика одинарной точности. Когда значение менее точного плавающего типа преобразуется к равному или более точному плавающему типу, значение не меняется. Когда значение более точного плавающего типа преобразуется к менее точному плавающему типу и оно находится в границах представления, результат может быть следующим большим или предшествующим меньшим представимым значением.
4.4. Плавающие и целочисленные типы.
Преобразование значения плавающего типа к целочисленному типу сводится к обрубанию, т.е. отбрасыванию дробной части. Результат не определен, если значение не может быть представлено этим целочисленным типом.
Преобразование значения целочисленного типа к плавающему типу математически настолько точно, насколько позволяет аппаратура.
4.5. Арифметические преобразования
1. Если один операнд имеет тип long double, другой операнд приводится к long double.
2. В противном случае, если один операнд имеет тип double, другой операнд приводится к double.
3. В противном случае, если один операнд имеет тип float, другой операнд приводится к float.
4. В противном случае, над обоими операндами производятся целочисленные расширения.
4а. В этом случае, если один из операндов - unsigned long, другой преобразуется к unsigned long.
4б. В противном случае, если один операнд имеет тип long int, а другой - unsigned int, то, если long int может представить все значения unsigned int, последний преобразуется к long int; иначе оба операнда преобразуются к unsigned long int.
4в. В противном случае, если один операнд имеет тип long, другой операнд приводится к long.
4г. В противном случае, если один операнд имеет тип unsigned, другой операнд приводится к unsigned.
4д. В противном случае оба операнда принадлежат к типу int.
Система безопасных преобразований может быть представлена следующими цепочками:
signed char -> short -> int -> long

unsigned char -> unsigned shrt -> unsigned int -> unsigned long



float -> double -> long double
Любая реализация обычно имеет большее число безопасных преобразований.
4.6. Преобразования указателей
Над указателями при присваивании, инициализации, сравнении и другом использовании (8.2.1) могут быть выполнены следующие преобразования:
1. Константное выражение (5.19), равное нулю, преобразуется к пустому указателю (null pointer). Гарантируется, что этот указатель отличается от указателя на любой объект.
2. Указатель на переменный объект может быть преобразован к void*.
3. Укаазатель на функцию можно преобразовать к void*, если void* имеет достаточный размер, чтобы представить его.
4. Указатель на объект некоторого класса можно преобразовать к указателю на объект доступного базового класса (10), если преобразование однозначно (10.1); базовый класс считается доступным, если доступны его открытые (public) компоненты (11.1). Пустой указатель преобразуется в себя.
5. Выражение типа “массив элементов типа Т” можно преобразовать к указателю на начальный элемент этого массива.
6. Выражение типа “функция с результатом типа Т” можно преобразовать к указателю на “функцию с результатом типа Т”, за исключением случая, когда оно является операндом операции взятия адреса или операции вызова функции.
4.7. Преобразования ссылок
Следующие преобразования могут быть выполнены всякий раз, когда инициализируются ссылки (8.2.2):
Ссылку на класс можно преобразовать в ссылку на доступный базовый класс, если это преобразование однозначно (10.1.1). Результатом является ссылка на подобъект объекта производного класса.
4.8. Преобразования указателей на компоненты
Следующие преобразования могут быть выполнены над указателями на компоненты класса (8.2.3) при их использовании:
1. Константное выражение, (5.19), вычисленное равным 0, преобразуется в указатель на компонент, отличный от любого кругого указателя на компонент.
2. Указатель на компонент класса можно преобразовать в указатель на компонент класса, производного от данного, если при этом доступно (11.1) и однозначно обратное преобразование от указателя производного класса к улазателю базового класса (10.1.1).
Правило преобразования указателя на компонент (от указателя на компонент базового класса к указателю на компонент производного класса) является обратным по отношению к правилу для указателей на объекты (от указателя на производный к указателю на базовый (6.4, 10). Эта инверсия необходима для поддержки системы типов данных.

5. Выражения
Выражение есть последовательность знаков операций и операндов, задающая вычисление. Выражение может вырабатывать результирующее значение и вызывать побочные эффекты.
Порядок вычисления подвыражений определяется правилами старшинства и группирования операций. Обычные математические правила для ассоциативных и коммутативных операций можно применять только к действительно ассоциативным и коммутативным операциям. Исключая то, что оговорено особо, порядок вычисления операндов в операции не определен. В частности, если объект модифицируется в выражении дважды, результат не определен, кроме случаев, когда порядок гарантируется составляющими выражение операциями.
5.1 Первичные выражения
Первичными выражениями являются литералы, имена, а также имена уточненные знаком операции указания области действия “::” (scope resolution operator).
первичное-выражение:

литерал


this

:: идентификатор

:: имя-функции-операции

:: уточненное-имя

(выражение)

имя
Литерал (literal) есть первичное выражение, тип которого определяется видом литерала (2.5).


В теле компонентной функции класса (9.3) ключевое слово this обозначает указатель на объект, для которого эта функция вызвана. Вне тела компонентной функции класса это ключевое слово употребляться не может.
Операция “::” вместе с сопровождающим идентификатором, именем-функции-операции или уточненным-именем образуют первичное выражение, тип которого определяется типом операнда. Результат операции “::” - идентификатор, имя или

имя-функции-операции. Область действия операнда должен быть файл. Операция дает возможность обращаться к операнду, даже если его идентификатор скрыт. Пример:


int x;

void f()

{

int x = 1; //скрывает глобальное x

::x = 2; // присваивает глобальному x

}
Выражение в круглых скобках есть первичное выражение, чей тип и значение идентичны такому же выражению без скобок.
Имя есть та форма выражения, в которой оно может поямляться после операций “.” и “->“(5.2.4).

имя:


идентификатор

имя-функции-операции

имя-функции-приведения

~ имя-класса

уточненное-имя
Идентификатор есть имя, если он объявлен соответствующим образом (7). Имя-функции-операции и имя-функции-приведения объясняютя в 13.4 и 12.3.2, соответственно. Имя-класса с предшествующей тильдой обозначает деструктор (12.4).
уточненное-имя:

уточненное-имя-класса :: имя


Уточненное-имя-класса (9.1) со следующим за ним “::” и далее именем компонена этого класса (9.2) или компонента базового класса (10) составляют уточненное-имя; его тип есть тип компонента класса. Результатом операции является этот компонент класса. Когда встречается имя-класса :: имя-класса или имя-класса :: ~ имя-класса, оба имени-класса должны быть именем одного и того же класса; эти конструкции обозначают соответственно имена конструкторов (12.1) и деструкторов (12.4).
5.2. Постфиксные выражения
Постфиксные выражения группируются слева направо.
постфиксное-выражение:

первичное-выражение

постфиксное-выражение [выражение]

постфиксное-выражение (список-выражений opt)

имя-простого-типа (список-выражений opt)

постфиксное-выражение . имя

постфиксное-выражение -> имя

постфиксное-выражение ++

постфиксное-выражение --
список-выражений:

выражение-присваивания

список-выражений , выражение-присваивания
5.2.1. Индексация
Постфиксное выражение, за которым следует выражение в квадратных скобках, называется индексацией (subscription). Первое выражение должно быть типа “указатель на Т”, а второе - целочисленного типа. Тип результата есть Т. Выражение Е1[Е2] идентично по определению *((E1)+(E2)). См. 5.3 и 5.7 относительно операций * и + и 8.2.4 - относительно массивов.
5.2.2. Обращение к функции
Вызов функции состоит из постфиксного выражения, за которым в круглых скобках идет список выражений (фактических параметров), разделенных запятыми. Постфиксное выражение должно иметь один из следующих типов: “функция с результатом типа Т”, “указатель на функцию с результатом типа Т” или “ссылка на функцию с результатом типа Т”. Результат обращения к функции имеет тип Т.
При вызове функции каждый формальный параметр инициализируется своим фактическим параметром. Выполняютя стандартные преобразования (4) и преобразования, определенные пользователем (12.3). Функция может изменять значения своих неконстантных формальных параметров, но эти значения не влияют на значения фактических параметров вне области действия фукции, исключая случай, когда неформальный параметр является неконстантной ссылкой (8.2.2). Значение неконстантных объектов можно также модифицировать через параметры-указатели.
Функция может быть также вызвана с меньшим числом фактических параметров (умолчания для параметров) или с большим (если в объявлении задано многоточие, 8.2.5), чем задано в описании функции (8.3).
Порядок вычисления параметров не определен. Порядок вычисления постфиксного выражения и списка выражений параметров также не определен.
Подробнее механизм подстановки параметров объяснен в разделе 8.3.
5.2.3. Явное преобразование типа
Иногда бывает необходимо явно преобразовать значение одного типа в значение другого. Имя-простого-типа (7.1.6), за которым следует список-выражений (возможно пустой) в круглых скобках, конструирует значение указанного типа по списку выражений. Если список содержит больше одного значения, тип должен быть классом с соответственно объявленным конструктором (8.4, 12.1). Если тип является классом с соответственно объявленным конструктором, будет вызван этот конструктор, в противном случае результатом является неопределенное значение данного типа. Пример:
class complex {

// ...

public:

complex(double, double);

}

f()

{

complex z = complex(1,2);

int i = int(); // i получает неопределенное значение

float r = float(1); // 1 преобразуется в 1.0

}
Таким образом, в С++ сохраняется традиционная в С запись приведения, например, (double)x и добавляется функциональная запись, например double(x).

Функциональная запись не может применяться для типов, не имеющих простого имени. Например, чтобы преобразовать значение к указательному типу, надо или использовать приведение типа


char* p = (char*)0777;
или определить новое имя типа:
typedef char* Pchar;

char* p = Pchar(0777);
Когда преобразование типа не является необходимым, его лучше избегать. Программы, в которых используется много явных преобразований типов, труднее понимать, чем те, в которых этого нет. Явные преобразования типа, однако, позволяют избежать многих ошибок, возможных при использовании бестипового языка.
5.2.4. Доступ к компоненте объекта
Постфиксное выражение, сопровождаемое точкой и далее именем, образует постфиксное выражение. Выражение должно быть объектом класса, а имя - именем компонента этого класса. Результатом является названный компонент объекта.
Постфиксное выражение, сопровождаемое знаком “->“ и далее именем, также образует постфиксное выражение. Выражение должно быть указателем на объект класса, а имя - именем компонента этого класса. Результатом является названный компонент объекта.
Понятие “объект класса” включает объекты структур (9.2) и объединений (9.5).
5.2.5. Увеличение и уменьшение
Операция “++”, употребленная после операнда, поставляет исходное значение операнда и затем увеличивает его на единицу; операнд должен быть l-значением арифметического или указательного типа.
Операция “--”, употребленная после операнда, поставляет исходное значение операнда и затем уменьшает его на единицу; операнд должен быть l-значением арифметического или указательного типа.
Основное назначение операций “++” и “--” заключается в пошаговом продвижении по массиву. В следующем примере строка по указателю p, завершающяяся нулем, копируется в область по указателю q.
while (*q++ = *p++);
5.3. Унарные операции
Выражение с унарными (префиксными) операциями группируется справа налево.
унарное-выражение:

постфиксное-выражение

++ унарное-выражение

-- унарное-выражение

унарная-операция выражение-приведения

sizeof унарное-выражение

sizeof (имя-типа)

выражение-размещения



выражение-освобождения
унарная-операция: одно из * & + - ! ~
Унарная операция “*” означает разыменование (indirection): операнд должен вырабатывать указатель, а результат есть l-значение, именующее объект (адрес объекта), на который указывал операнд. Если тип операнда есть “указатель на Т”, тип результата будет Т. Пример:
char c1 = 'a';

char* p = &c1; // в p хранится адрес c1

char c2 = *p; // c2 = 'a'
Здесь переменная, на которую указывает р, это - с1, а значение, которое хранится в с1, это р, поэтому присваиваемое с2 значение есть 'a'.
Результат унарной операции “&” (“взять адрес”) есть указатель на операнд. Операнд должен быть именем функции, l-значением или уточненным-именем. В первых двух случаях, если тип операнда есть Т, результат будет иметь тип “указатель на Т”. В случае уточненного-имени, если компонент нестатический и имеет тип Т в классе С, у результата будет тип “указатель на компонент типа Т в классе С”. Для статического компонента типа Т тип результата есть просто “указатель на Т”.
Операнд унарной операции “+” должет иметь арифметический или указательный тип, а результат будет равен значению аргумента. Операция принадлежит истории и, вообще говоря, бесполезна.
Операнд унарного минуса “-”должет иметь арифметический тип, а результат будет равным операнду с обратным знаком.
Операнд операции логического отрицания “!”должет иметь арифметический или указательный тип, а результат будет равен 1 если значение операнда равно 0, и 0, если оно отлично от нуля. Тип результата есть int.
Операнд операции “~”должет иметь целочисленный тип; результат операции есть побитовое дополнение операнда.
5.3.1. Операции увеличения и уменьшения
Операция “++”, употребленная перед операндом, увеличивает его на единицу и поставляет это значение; операнд должен быть l-значением арифметического или указательного типа. Выражение ++x эквивалентно выражению x+=1.
Пример (функция, подсчитывающая число символов в строке, не считая завершающего нуля):
int strlen(char* p)

{

int i = 0;

while (*p++) i++;

return i;

}
Операция “--”, употребленная перед операндом, уменьшает его на единицу и поставляет это значение; операнд должен быть l-значением арифметического или указательного типа.
5.3.2. Операция sizeof (размер)
Результатом операции является размер ее операнда в байтах. Операнд должен быть либо унарным выражением (которое не вычисляется), либо именем типа в скобках.
Результатом применения операции к ссылке является размер именуемого ею объекта.
В применении к классу операция дает число байтов в представлении этого класса.
Результат операции имеет тип size_t - целочисленный беззнаковый тип, определенный в стандартном файле-заголовке и зависящий от реализации.
5.3.3. Выражение размещения (операция new)
Операция new создает объект типа имя_типа (8.1), к которому она применена. Этот тип должен быть типом объекта, т.е. функция не может быть размещена при помощи этой операции, хотя допустимо применение операции к типу указателя на функцию. В упрощенном виде выражение-размещения имеет следующий синтаксис:
выражение-размещения:

:: opt new имя-типа инициализация-new opt

инициализация-new:

(список-инициализаторов opt)


Выражение-размещения обозначает следующее:

1. Отведение памяти под объект указанного типа.

2. Инициализация объекта.

3. Возвращение указателя на объект.


Время жизни объекта, созданного операцией new, не ограничено областью действия, в которой он был создан. Когда создается массив, возвращается указатель на его начальный объект. Например, new int и new int[10] возвращают int*.
Вызов операции new T приводит к вызову функции operator new() с аргументом sizeof(T) для получения области памяти (12.5) достаточной для размещения объекта типа Т.
Когда операция new создает объект, не являющийся классом, используется глобальная функция-операция ::operator new(). Когда операция new создает объект класса Т, используется Т::operator new(), если она существует; в противном случае используется глобальная ::operator new(). Употребление ::new гарантирует, что будет вызвана глобальная ::operator new(), даже если Т::operator new() существует.
В выражении-размещения может быть указана инициализация-new. Для объектов классов с конструктором (12.1) этот список параметров будет использован в вызове конструктора; в противном случае инициализация должна иметь вид (выражение) или (). В первом случае выражение будет использовано для инициализации объекта, во втором случае значение объекта остается неопределенным. Пример:
class complex {

// ...

public:

complex(double, double);

// ...

}

void f()

{

complex* pc = new complex(1,2);

int* pi = new int(7);

double* pd1 = new double(); // неопределенное значение

double* pd2 = new double; // неопределенное значение

}
Операция new служит для создания объектов на определенное время, пока они нужны. В частности, во многих случаях полезно создать объект, который можно использовать после возврата из функции, в которой он создается. Об объектах, созданных операцией new, говорят, что они располагаются в свободной (динамической) памяти. Такими объектами обычно бывают вершины деревьев или элементы связанных списков, являющиеся частью большой структуры данных, размер которой не известен на стадии компиляции.
5.3.4. Выражение-освобождения (операция delete)
Операция delete уничтожает объект, созданный операцией new.
выражение-освобождения:

:: opt delete выражение-приведения

:: opt delete [] выражение-приведения
Результат имеет тип void. Операндом должен быть указатель, ранее поставленный операцией new. Эффект применения операции delete к указателю, не являющемуся результатом операции new, не определен и обычно ужасен. Гарантируется, однако, безапасность выполнения операции над нулевым указателем. Пример:
void f()

{

int I;

int* p = &I;

delete p; // ошибка

p = new int[10];

p++;

delete p; // ошибка

p = 0;

delete p; // допустимо

}
Чтобы освободить память, операция delete будет вызывать функцию operator delete() (12.5). Для объекта, не являющегося объектом класса, вызывается глобальная operator delete(). Для объекта класса Т вызывается Т::operator delete(), если она существует; в противном случае используется глобальная ::operator delete(). Употребление операции ::delete гарантирует вызов глобальной ::operator delete(), даже если в классе имеется Т::operator delete().
Для освобождения массивов используется форма




Поделитесь с Вашими друзьями:
1   2   3   4   5   6   7   8   9   10


База данных защищена авторским правом ©vossta.ru 2019
обратиться к администрации

    Главная страница