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



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


С++
1. Введение

1.1 Обзор языка
Язык С++ был создан Американским ученым Бьярном Строуструпом в первой половине 80-х годов. Задачи:

- улучшить С

- поддержать абстракцию данных

- поддержать объектно-ориентированное программирование.


Улучшение С выразилось в:

- контроль типов параметров

- операции работы с памятью (динамич. создание и уничтожение объектов)

- совмещение (overloading) имен функций и операций

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

- отвечает большинству задач системного программирования,

- идет везде и на всем,

- может быть использован в среде программирования UNIX.


Абстракция данных есть метод разработки программ с представлением в ней понятий из прикладной области как пользовательских (user-defined) типов данных. Интерфейс типа данных (спецификация) отделяется от его реализации, что

- облегчает понимание программ, позволяя прямо описывать понятие в

естественных для него терминах,

- изменять реализацию типа данных, не задевая пользователей.


С++ обеспечивает абстракцию данных поддерживая следующие понятия и возможности:

- конструктор пользовательских типов class

- средства управления доступом (public, private)

- абстрактные классы

- гарантированная инициализация и очистка объектов

- пользовательские преобразования типов объектов

- параметризованные (родовые) функции и типы данных

- обработка исключительных ситуаций.


Ключевым понятием в С++ является класс. Классы обеспечивают перечисленные выше возможности.
ООП основывается на иерархической организации классов подобно тому, как устанавливается иерархия понятий в реальной жизни. В С++ оно поддерживается понятиями

- производный класс (подкласс)

- виртуальная функция (подмена функций).
Концепция класса вместе с производными классами и виртуальными функциями была взята из языка Симула 67. Возможности совмещения имен и расположения описаний везде, где может встретиться оператор, взяты из языка Алгол 68. Родовые конструкции (generics) появились под влиянием языков Клу и Ада. Из Ады взят также механизм обработки исключительных ситуаций.
1.2. Нотация для синтаксиса
Синтаксические понятия выделяются курсивом, а литеральные слова и символы (терминальные понятия) - жирным шрифтом. Альтернативы перечисляются на отдельных строчках, кроме немногих случаев, в которых большое количество альтернатив представлено на одной строчке, помеченной фразой “одно из”. Факультативное понятие помечается индексом opt, так что

{выражение opt}

означает возможное выражение, заключенное в фигурные скобки.

2. Лексические соглашения
Программа на С++ состоит из одного или нескольких файлов (3.3). Файл представлят собой фрагмент текста, содержащий исходную программу на С++ с командами прероцессора. Концептуально каждый файл транслируется в несколько проходов. На первом проходе выполняется препроцессирование (16): в частности, на этом этапе производятся сборка файлов и макроподстановки. Препроцессирование управляется директивами препроцессора. Ими считаются строки, начинающиеся с литеры #. Результатом препроцессирования является последовательность лексем, называемая единицей трансляции (translation unit).
2.1. Лексемы
Имеется 5 видов лексем: идентификаторы, ключевые слова, литералы, знаки операций и прочие разделители. Пробел, символы табуляции, новой строки и протяжки страницы, а также комментарии (обобщенно - символы пустых промежутков) служат для разделения лексем. Каждая очередная лексема выбирается как наиболее длинная строка, начиная с данной литеры, которая может образовать лексему.
2.2. Комментарии
Сочетание литер /* начинает, а сочетание литер */ заканчивает комментарий. Комментарии нельзя вкладывать друг в друга. Литеры // открывают комментарий, который который заканчивается в конце той же строки.
2.3. Идентификаторы
Идентификатор (имя) - это последовательность букв, цифр и символа “_”, начинающаяся с буквы (символ “_” считается буквой). Большие и малые буквы считаются различными. Имена, начинающиеся с подчерка, по традиции зарезервированы и используются специальным образом в системах, обеспечивающих выполнение программ.
2.4. Ключевые слова
Следующие идентификаторы могут употребляться только как ключевые слова:
asm continue float new signed try

auto default for operator sizeof typedef

break delete friend private static union

case do goto protected struct unsigned

catch double if public switch virtual

char else inline register templete void

class enum int return this volatile

const extern long short throw while


Для обозначения операций и в качестве знаков препинания используются следующие символы и их комбинации:
! % ^ & * ( ) - + = { } | ~

[ ] \ ; ‘ : “ < > ? , . /

-> ++ -- .* ->* << >> <= >= == != &&

|| *= /= %= += -= <<= >>= &= ^= \= ::


Две лексемы обрабатывает препроцессор: # ##
2.5. Литералы
Существует несколько видов литералов (часто называемые константами).
литерал:

целая-константа

литерная-константа

плавающая-константа

литеральная-строка
5.2.1. Целые константы
Целая константа состоит из последовательности десятичных цифр, начинающейся не с нуля. Последовательность цифр без 8 и 9, начинающаяся с нуля, рассматривается как восьмеричное число. Последовательность цифр, начинающаяся с 0х или 0Х и возможно включающая буквы от А (а) до F (f), рассматривается как шестнадцатиричное число. Пример:

двенадцать: 12 014 0XC
Тип целой константы зависит от ее формы, значения и суффикса. Если она является десятичной и не содержит суффикса, то имеет первый из следующих типов, в котором может быть представлено ее значение:

int, long int, unsigned long int.

Восьмеричная или шестнадцатиричная константа без суффикса имеет первый из следующих типов, в котором может быть представлено ее значение:



int, unsigned int, long int, unsigned long int.

Если константа содержит суффикс u или U, она имеет первый из следующих типов, в котором может быть представлено ее значение:



unsigned int, unsigned long int.

Если константа содержит суффикс l или L, она имеет первый из следующих типов, в котором может быть представлено ее значение:



long int, unsigned long int.

Если константа имеет один из суффиксов ul, lu, uL, Lu, Ul, lU, UL, LU, ее типом является unsigned long int.


2.5.2. Литерные константы
Литерная константа представляет собой последовательность из одной или нескольких литер, заключенных в одинарные кавычки, как, например, ‘x’. Литерная константа, содержащая одну литеру, имеет тип char. Многолитерные константы имеют тип int. Значение многолитерной константы зависит от реализации и потому их лучше избегать.
Специальный символ \ служит для представления неграфических литер:
новая строка (new line) NL (LF) \n

горизонтальная табуляция (horisontal tab) HT \t

вертикальная табуляция (vertical tab) VT \v

возврат на символ (backspace) BS \b

возврат каретки (carriage return) CR \r

протяжка страницы (form feed) FF \f

внимание (alert) BEL \a

обратная косая черта (backslash) \ \\

вопросительный знак (question mark) ? \?

одинарная кавычка (single quote) ‘ \’

двойные кавычки (double qoute) “ \”

восьмеричное число (octal number) ooo \ooo

шестнадцатиричное число (hex number) hhh \xhhh
Последовательности типа \ооо или \xhhh используется для спецификации кода литеры. В случае превышения допустимого значения типа char значение литерной константы определяется реализацией.
2.5.3. Константы с плавающей точкой
Константа с плавающей точкой состоит из целой части, десятичной точки и дробной части, литеры e или E, возможного целого показателя степени с возможным знаком и возможного суффикса. Либо целая часть, либо дробная часть (но не обе сразу) может быть опущена. Плавающая константа имеет тип double, если только он не задан явно суффиксом. Суффиксы f и F задают тип float, суффиксы l и L задают тип long double. Примеры:

double: 2.0, 2., 0.2e1, .2E1

float: 2.0F, 20e-1F

long double: 2.0L
В середине константы с плавающей точкой не может быть пробела. Например, 65.43 е-21 рассматривается как четыре отдельные лексемы:

65.43 е - 21

и считается синтаксической ошибкой.
2.5.4. Литерные строки
Литерная строка представляет собой последовательность из одной или нескольких литер, заключенных в двойные кавычки, например, “...”. Строка имеет тип “массив литер” (array of char) и статический класс памяти (3.5). В реализации конец строки помечается литерой \’0’. Размер строки равен числу ее литер, включая завершающий нуль. Например,

sizeof("asdf") == 5;

Строка "asdf" имеет тип char[5]. Пустая строка записывается как "" и имеет тип char[1].
Длинные строки могут быть разорваны пропуском (т.е. ' ', '\t', '\v', '\f', '\n' или комментарием) для того, чтобы сделать текст более четким. Пример:
char alfa[] = "abcdefghijklmnopqrstuvwxyz"

"ABCDEFGHIJKLMNOPQRSTUVWXYZ";


Такие смежные строки сцепляются, поэтому alfa могла бы быть эквивалентно инициализирована одной строкой
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
2.5.5. Нуль
Нуль имеет тип int. Благодаря стандартным преобразованиям (4), его можно употреблять как константу любого целого, плвающего или указательного типа. Тип нуля определяется контекстом.

3. Основные понятия
Имя может обозначать объект, функцию, множество функций, перечислитель, тип, член класса, шаблон, значение или метку. Имя вводится в программу посредством объявления. Имя может быть употреблено только внутри ее области действия (scope). Каждое имя имеет тип, который определяет способ его употребления.
Объект есть область памяти (3.7). Именованный объект имеет класс памяти (3.5), задающий его время жизни. Смысл значения объекта определяется типом выражения, использованного для доступа к нему.
3.1. Объявления и описания
Оъявление (7) вводит в программу одно или несколько имен. Объявление является описанием за исключением следующих случаев:

1) оно объявляет функцию без спецификации тела (8.3),

2) содержит спецификатор extern (7.1.1) при отсутствии инициализатора и

тела функции,

3) является объявлением статического компонента в объявлениии класса (9.4),

4) является объявлением имени класса (9.1),

5) является объявлением имени типа (typedef) (7.1.3).
Примеры описаний:

int a;

extern const c = 1;

int f(int x) {return x+a;};

struct S {int a; int b;};

enum {up, down};
Примеры чисто объявлений:

extern int a;

extern const c;

int f(int);

struct S;

typedef int Int;
В программе должно быть ровно одно описание каждого объекта, функции, класса и перечислителя, в ней использованных (объявления могут повторяться).
3.2. Области действия
Существует 4 разновидности областей действия: локальная, функция, файл и класс.
Локальная: Имя, объявленное в блоке (6.3), локализовано в этом блоке и может быть использовано только в нем и в блоках, вложенных в него, начиная с места объявления. Имена формальных параметров функции считаются объявленными в самом внешнем блоке тела этой функции.
Функция: Метки (6.1), объявленные в теле функции, могут быть использованы повсюду в теле функции.
Файл: Имя, объявленное вне всех блоков (6.3) и классов (9), можно использовать в транслируем файле, содержащем это объявление, начиная с места объявления. Такое имя называется глобальным (global).
Класс: Имя компонента класса локализуется в этом классе и может быть использовано только:

1) в компонентной функции этого класса (9.3),

2) после операции “.” (точка), примененной к объекту этого класса (5.2.4),

или его производного класса (10),

3) после операции ->, примененной к указателю на объект этого класса

(5.2.4) или производного от него,

4) после операции :: (5.1), примененной к имени этого класса или

производного от него.



Имя, объявленное как дружественное (friend, 11.4), считается глобальным; то же самое справедливо для имени класса, впервые объявленного в качестве типа результата функции или типа параметра.
При повторном объявлении имени во вложенном блоке или классе предыдущее объявление оказывается скрытым; после выхода из блока имя вновь обретает прежний смысл. Скрытое имя компонента класса все же можно употреблять, если его уточнять именем класса при помощи операции :: (5.1, 9.4, 10). Скрытое имя объекта, функции, типа и перечислителя, у которого областью действия является файл, можно использовать посредством унарной операции :: (5.1). Пример:
int g = 99;

f(int g) {return g ? g : ::g} //в последнем случае используется глобальное g
3.3. Программа и сборка
Пограмма состоит из одного или нескольких файлов, собранных вместе. Файл состоит из последовательности объявлений. Глобальное имя, объявленное как статическое (static), локализовано в своей единице трансляции и может быть использовано в других файлах для именования других объектов и т.п. Такое имя называется внутренним (internal linkage). Имя, объявленное как встраиваемое (inline) или как const но без extern, также локализовано в своей единице трансляции.
Каждое объявление глобального имени, не являющегося внутренним, приводит в многофайловой программе к одному и тому же объекту (3.7), функции (8.2.5) или классу (9). Такое имя называется внешним (external linkage). Внешним является также имя, явно объявленное как extern.
Типы во всех объявлениях внешнего имени должны быть идентичны. Программа должна содержать ровно одно описание каждой функции, объекта, класса или перечислителя.
3.3.1. Компоновка
Иметь всю программу в одном файле обычно невозможно, поскольку подпрограммы стандартных библиотек и операционной системы находятся в другом месте. Кроме того, хранить большую программу в одном файле непрактично и неудобно: поскольку файл является единицей трансляции, во всех случаях, когда в него вносятся изменения, весь файл надо транслировать заново.
Чтобы обеспечить раздельную компиляцию, программист должен сделать объявления, дающие информацию о типах, необходимую для анализа файла независимо от остальной программы. Для этого объявления всех имен должны быть согласованы. Рассмотрим два файла:
// file1.c:

int a = 1;

int f() { /* что-то делает */}
// file2.c:

extern int a;

int f();

void g() { a = f(); }
Переменная а и функция f(), которые использует функция g() в файле file2.c, - такие же, как и те, что определены в файле file1.c. Ключевое слово extern указывает, что объявление а в file2.c является только объявлением, а не описанием. Объект в программе должен описываться только один раз, объявляться он может много раз, но все объявления должны быть согласованы. Пример:
// file1.c:

int a = 1;

int b = 1;

extern int c;
// file2.c:

int a;

extern double b;

extern int c;
Здесь три ошибки: а описано дважды (int a; является описанием, которое означает int a = 0;), b объявлено дважды с разными типами, а с объявлено дважды, но не описано.
Другая картина при использовании описателя static:
// file1.c:

static int a = 6;

static int f() { /* . . . */}
// file2.c:

static int a = 7;

static int f() { /* . . . */}
Поскольку каждое а и f объявлено как static, программа правильна.
3.3.2. Заголовочные файлы
Обним из простых способов достижения согласованности определений различных файлов является включение в исходные файлы программы заголовочных файлов с интерфейсной информацией. Директива

#include "to-be-included"

замещает себя содержимым файла "to-be-included", которым должен быть исходный текст на С++. Такое включение обычно делается препроцессором С++. Для включения файлов из стандартного каталога вместо кавычек используются угловые скобки "<" и ">". Пример:
#include // из стандартного каталога

#include "myheader.h" // из текущего каталога
В заголовочном файле могут содержаться:
Определения типов struct point {int x, y;};

Параметризованные типы templete class V;

Объявления функций extern int strlen(const char*);

Описания встраиваемых

функций inline char get() {return *p++};

Одъявления данных extern int a;

Описания констант const float pi = 3.14.1593;

Перечисления enum bool {false, true};

Объявления имен class Matrix;

Директивы включения #include

Определения макросов #define Case break; case

Комментарии /* проверка на конец файла */


Заголовочные файлы обычно имеют суффикс .h, а файлы, содержащие функции или определения данных - суффикс .с.
3.4. Старт и завершение
Пограмма должна содержать функцию с именем main(). Эта функция предназначается для входа в программу. Она не может быть вызвана из программы. Нельзя также брать ее адрес и объявлять ее как inline или static.
Вызов функции void exit(int), объявленной в , завершает программу. Значение параметра возвращается окружению как результат работы программы. Оператор return в main() приводит к вызову exit() с возвращаемым значением как фактическим параметром.
Обычно программа осуществляет какой-либо вывод. Пример:
#include

int main()

{

cout << "Hello, world!\n";

}
Здесь строка #include сообщает компилятору о необходимости использования стандартных средств ввода-вывода, находящихся в файле iostream.h. Операция "<<" ("поместить в") переписывает второй аргумент в первый (в данном случае, строку "Hello, world!\n" в стандартный поток cout. Значение типа int, возвращаемое функцией main(), программа передает окружающей среде. Если ничего не случается, среда получает случайное значение.
3.5. Классы памяти
В языке имеется два объявляемых класса памяти: автоматический и статический.

Автоматические (или локальные) объекты локализованы относительно

каждого входа в блок.


Статические объекты существуют и сохраняют свои значения в течение всего

времени выполнения программы.


Локальные объекты инициализируются (12.1) всякий раз при исполнении их описаний и разрушаются (12.4) при выходе из содержащих их блоков (6.7).
Статический глобальный объект (любой объект, описанный на уровне файла) инициализируется в единице трансляции до первого использования любой функции или объекта, определенного в ней. Инициализация локальных статических объектов (со спецификатором static) будет объяснена в 8.4.
3.6. Типы данных
Каждое имя (идентификатор ) в С++ программе имеет ассоциированный с ним тип, который определяет: какие операции можно применить к имени (т.е. к объекту, на который он ссылается) и как эти операции интерпретируются. Примеры:
int error-number;

float real(complex* h);
Поскольку error-number объявлено как int, его можно присваивать, использовать в арифметических выражениях и т.д. Что касается функции real, то она может вызываться с адресом complex в качестве параметра.
Имена вроде int и float являются именами типа. Обычно имя типа используется в объявлениях для спецификации других имен. Допускается еще два действия над именами типов: sizeof (для определения объема памяти, требуемой для размещения объекта данного типа) и new (для размещения объекта данного типа в свободной памяти). Пример:
int main()

{

int* p = new int;

cout << "sizeof(int) = " << sizeof(int) '\n';

}
Различаются два вида типов данных в С++: основные, или базовые (fundemental), и производные (derived).
3.6.1. Основные типы
Имеется несколько основных типов и несколько их разновидностей. Они соответствуют наиболее общим и широко используемым основным аппаратным единицам памяти компьютера.
А) Литерные типы
Объект, объявленный как литера (char) может содержать любую литеру из набора литер реализации. Литерные объекты могут быть явно обозначены как unsigned (без знака) или signed (со знаком). Представление всех трех типов определяется реализацией.
Б) Целые типы
Существует три вида целых типов: short int, int и long int. Представление зависит от реализации, но обычно длинные целые занимают не меньше памяти, чем короткие целые. Целые, объявленные как unsigned служат для представления натуральных чисел.
Поскольку int - это по умолчанию тип со знаком, знаковые целые есть просто сининимы обычных целых. Для компактности можно опускать int в комбинациях: что не меняет смысла, так как long означает long int, и тип unsigned означает unsigned int. Вообще, когда в объявлении опущен тип, предполагается int.
В) Плавающие типы

Существует три вида плавающих типов: float, double и long double. Тип double обеспечивает не меньшую точность представления, чем float, а тип long double - не меньшую, чем double.


Типы char, int всех размеров, а также перечисления (7.2) называются целочисленными (integral) типами. Целочисленные и плавающие вместе составляют арифметические типы. Типичные размеры в реализации (в байтах):
char 1

short int 2

int 2 или 4

long int 4 или 8

float 4

double 8


long double 12 или 16
Тип void определяет пустое множество значений. Он задается в качестве типа результата функции, не возвращающей значения, или как базовый тип для указателей на объекты неизвестного типа. Примеры:
void f(); // f не возвращает значения

void* pv; // указатель на объект неизвестного типа
Переменной типа void* можно присвоить указательное значение любого типа. Это применяется главным образом для передачи указателей функциям, которые не позволяют сделать предположение о типе объекта, и для возврата из функций нетипизированных обектов. Чтобы использовать такой объект, необходимо применить явное преобразование типа. Подобные функции находятся на самом нижнем уровне системы, там где осуществляется работа с основными аппаратными ресурсами. Пример:
void* malloc(unsigned size);
void free(void*);
void f() // выделение памяти в стиле C

{

int* pi = (int*)malloc(10*sizeof(int));

char* pc = (char*)malloc(10);

// . . .

free(pi);

free(pc);

}
Здесь запись (type)exprssion задает преобразование expression к типу type.




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


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

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