Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Programmirovanie

.pdf
Скачиваний:
16
Добавлен:
27.05.2015
Размер:
785.71 Кб
Скачать

1.

При выполнении цикла while сначала проверяется условие. Если оно ложно, то цикл не выполняется и управление передается на следующую инструкцию после тела цикла while. Если условие истинно, то выполняется инструкция, после чего условие проверяется снова и снова выполняется инструкция. Так продолжается до тех пор, пока условие будет истинно.

while (условие)

{

блок инструкций

}

Цикл " while " с постусловием отличается от цикла с предусловием тем, что сначала выполняется блок цикла, а потом проверяется условие. Если условие истинно, то цикл будет выполнен еще раз, и так до тех пор, пока условие будет истинно.

do

{

Блок инструкций

}

while (условие);

Поскольку условие проверяется после выполнения тела цикла, то блок цикла с постусловием всегда будет выполнен хотя бы один раз, независимо от истинности условия.

При использовании цикла for необходимо задать три параметра (в круглых скобках через точку с запятой). Первый параметр – начальное значение переменной, задается в виде присваивания переменной значения. Второй параметр – условие выхода из цикла. Цикл будет исполняться, пока условие истинно.

Третий параметр – шаг изменения переменной. for ((начальное значение переменной); (условие); (шаг изменения переменной))

{

блок инструкций

}

Оператор ветвления — оператор, обеспечивающая выполнение определённой команды (набора команд) только при условии истинности некоторого логического выражения. if (условие)

{

операторы

}

else

{

Операторы

}

Оператор switch предназначен для организации выбора из множества различных вариантов. Формат оператора следующий:

switch ( выражение ) { [объявление]

:

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

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

:

:

[ default: [ список операторов ]]

}

Выражение, следующее за ключевым словом switch в круглых скобках, может быть любым выражением, допустимыми в языке СИ, значение которого должно быть целым. Тело оператора smitch состоит из нескольких операторов, помеченных ключевым словом case с последующим константным-выражением. Все константные выражения в операторе switch должны быть уникальны. Кроме операторов, помеченных ключевым словом case, может быть, но обязательно один, фрагмент помеченный ключевым словом default.

Список операторов может быть пустым, либо содержать один или более операторов. Причем в операторе switch не требуется заключать последовательность операторов в фигурные скобки.

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

2.

Функция - это совокупность объявлений и операторов, обычно предназначенная для решения определенной задачи. Каждая функция должна иметь имя, которое используется для ее объявления, определения и вызова. В любой программе на СИ должна быть функция с именем main (главная функция), именно с этой функции, в каком бы месте программы она не находилась, начинается выполнение программы.

При вызове функции ей при помощи аргументов (формальных параметров) могут быть переданы некоторые значения (фактические параметры), используемые во время выполнения функции. Функция может возвращать некоторое (одно !) значение. Это возвращаемое значение и есть результат выполнения функции, который при выполнении программы подставляется в точку вызова функции, где бы этот вызов ни встретился. Допускается также использовать функции не имеющие аргументов и функции не возвращающие никаких значений. Действие таких функций может состоять, например, в изменении значений некоторых переменных, выводе на печать некоторых текстов и т.п.. Определение функции задает тип возвращаемого значения, имя функции, типы и число формальных параметров, а также объявления переменных и операторы, называемые телом функции, и определяющие действие функции. для того, чтобы компилятор мог осуществить проверку соответствия типов передаваемых фактических параметров типам

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

Объявление функции имеет такой же вид, что и определение функции, с той лишь разницей, что тело функции отсутствует. Функция не может возвращать массив или функцию, но может возвращать указатель на любой тип, в том числе и на массив и на функцию. Тип возвращаемого значения, задаваемый в определении функции, должен соответствовать типу в объявлении этой функции.

Функция возвращает значение если ее выполнение заканчивается оператором return, содержащим некоторое выражение

3.

Переменные, в которых сохраняются параметры, передаваемые функции, также являются локальными для этой функции. Эти переменные создаются при вызове функции и в них копируются значения, передаваемые функции в качестве параметров. Эти переменные можно изменять, но все изменения этих переменных будут "забыты" после выхода из функции.

#include<iostream> using namespace std; void swap(int a, int b)

{

int t;

t=b; b=a; a=t;

}

int main()

{

int p=3,q=5; swap(p,q);

cout<<p<<" "<<q<<endl; return 0;

}

При вызове функции swap создаются новые переменные a и b, им присваиваются значения 3 и 5. Эти переменные никак не связаны с переменными p и q и их изменение не изменяет значения p и q. Такой способ передачи параметров называется передачей параметров по значению.

Чтобы функция могла изменять значения переменных, объявленных в других функциях, необходимо указать, что передаваемый параметр является не просто константной величиной, а переменной, необходимо передавать значения по ссылке. Для этого функцию swap следовало бы объявить следующим образом:

void swap(int & a, int & b)

Амперсанды перед именем переменной означают, что эта переменная является не локальной переменной, а ссылкой на переменную, указанную в качестве параметра при вызове функции. Теперь при вызове swap(p,q) переменные a и b являются синонимами для переменных p и q, и изменение их значений влечет изменение значений p и q.

4.

Массивы предназначены для хранения множества значений одного типа. Объявление int A[n] создает в памяти одномерный массив: набор пронумерованных элементов, идущих в памяти последовательно. Но можно создать и массив массивов следующим образом: int A[n][m]. Данное объявление создает массив из n объектов, каждый из которых в свою очередь является массивом типа int [m]. Можно объявлять не только двумерные массивы, но и массивы с большим количеством измерений. Например, объявление int A[n][m][l] создает трехмерный массив из n*m*l элементов. нумерация индексов начинается с нуля. Поэтому число индексов всегда на единицу меньше числа элементов массива. Имя массива – это указатель на его 0 элемент.

В С++ существует 2 типа строк.

Первый из них - это массив переменных типа char.

Если кто не помнит, то переменная типа char хранит в себе 1 символ. Размер такой строки равняется размеру массива - 1, т.к. последний элемент содержит NULL (пустая переменная без значения), который обозначает символ конца строки.

Например: char name[50];

По форме записи данная конструкция ничем не отличается от указателя на символьную переменную: char *S1; где char – тип указателя на символ, S1 – переменная-указатель. Для инициализации указателя требуется указать область памяти, где уже находится или будет находиться строка, при этом для строки должен быть выделен необходимый объем памяти.

Существует ряд способов инициализации указателя на строку:

·инициализация строковым литералом char *S1="Yes";

·присваивание значение другого указателя char *S1=S; где S – идентификатор массива или указатель на другую строку символов.

Указателю можно присваивать значение другого указателя: S1=S; где S1 – переменная типа указатель; S – строковая константа, идентификатор массива или указатель на другую строку символов.

Второй из вариантов, более удобный - это специальный класс string

Для его работы необходимо в начале программы подключить заголовочный файл string:

#include <string>

В отличии от типа char, string является классом. Для создания строки вам необходимо в начале программы написать using namespace std; Теперь чтоб создать строку достаточно написать:

string s; Для записи в строку можно использовать оператор =. s="Hello";

Часто возникают ситуации, когда заранее не известно, сколько объектов – чисел, строк текста и прочих данных будет хранить программа. В этом случае используется динамическое выделение памяти, когда память занимается и освобождается в процессе исполнения программы. При использовании динамической памяти (ДП) отпадает необходимость заранее распределять память для хранения данных, используемых программой. Управление динамической памятью – это способность определять размер объекта и выделять для его хранения соответствующую область памяти в процессе исполнения программы.

Для создания динамических переменных в языке C++ используются специальные операторы new и delete. Существуют две формы операторов: для одиночной переменной и массива значений.

Оператор new выделяет область памяти для одиночной переменной, размер которой соответствует ее типу, и возвращает указатель того же типа, так что дополнительных преобразований типа указателя не требуется: float * ptr; ptr=new float; В результате выполнения этого фрагмента программы в динамической памяти будет выделена область размером 4 байта для хранения одного значения типа float, и адрес этой области записан в переменную ptr. Для размещения в памяти нескольких значений одного типа (массива) применяется оператор new[]: float * ptrm; ptrm=new float[4]; при этом в квадратных скобках указывается количество элементов, для которых выделяется память. В результате выполнения этого фрагмента программы в динамической памяти будет выделена область размером 16 байт для хранения четырех чисел типа float, и адрес этой области записан в переменную ptrm. Для освобождения динамической памяти, выделенной при помощи new, используется оператор delete. Для одиночной переменной освобождение памяти имеет следующий вид: delete ptr; при этом освобождается область в динамической памяти размером 4 байта. Для освобождения памяти из-под массива, размещенного с помощью оператора new[], следует использовать следующую форму оператора delete: delete[] ptrm; Следует заметить, что недопустимо смешивать формы операторов new и delete: если память выделялась для одиночной переменной (new), то и освобождение должно производиться оператором delete, если в памяти размещался массив (new[]) – для освобождения памяти должен использоваться оператор delete[].

5.

Структура – тип данных, задаваемый пользователем. В общем случае при работе со структурами следует выделить четыре момента:

-объявление и определение типа структуры,

-объявление структурной переменной,

-инициализация структурной переменной,

-использование структурной переменной. Определение типа структуры представляется в виде struct ID

{

<тип> <имя 1-го элемента>; <тип> <имя 2-го элемента>;

…………

<тип> <имя последнего элемента>;

};

Определение типа структуры начинается с ключевого слова struct и содержит список объявлений, заключенных в фигурные скобки. За словом struct следует имя типа, называемое тегом структуры (tag – ярлык, этикетка). Элементы списка объявлений называются членами структуры или полями. Каждый элемент списка имеет уникальное для данного структурного типа имя. Однако следует заметить, что одни и те же имена полей могут быть использованы в различных структурных типах. Объявление переменной структурного типа имеет следующий вид:

struct ID var1;

при этом в программе создается переменная с именем var1 типа ID. Все переменные, использующие один шаблон (тип) структуры, имеют одинаковый набор полей, однако различные наборы значений, присвоенные этим полям. При объявлении переменной происходит выделение памяти для размещения переменной.

При размещении в памяти структурной переменной можно выполнить ее инициализацию. Неявная инициализация производится для глобальных переменных, переменных класса static. Структурную переменную можно инициализировать явно при объявлении, формируя список инициализации в виде константных выражений.

Формат: struct ID name_1={значение1, … значениеN};

Внутри фигурных скобок указываются значения полей структуры, например,

struct point pt={105,17};

при этом первое значение записывается в первое поле, второе значение – во второе поле и т. д., а сами значения должны иметь тип, совместимый с типом поля.

Над структурами возможны следующие операции:

-присваивание значений одной структурной переменной другой структурной переменной, при этом обе переменные должны иметь один и тот же тип;

-получение адреса переменной с помощью операции &;

-осуществление доступа к членам структуры. Присваивание значения одной переменной другой выполняется путем копирования значений соответствующих полей, например:

struct point pt={105,15},pt1; pt1=pt;

В результате выполнения этого присваивания в pt1.x будет записано значение 105, а в pt1.y – число 15. Работа со структурной переменной обычно сводится к работе с отдельными полями структуры. Доступ к полю структуры осуществляется с помощью операции. (точка) посредством конструкции вида: имя_структуры.имя_поля_структуры; при этом обращение к полю структуры представляет собой переменную того же типа, что и поле, и может применяться везде, где допустимо использование переменных такого типа. в структуре нельзя использовать методы. Класс это расширенная структура

6.

Указатель - это переменная, содержащая адрес некоторого объекта, например, другой переменной. Точнее - адрес первого байта этого объекта. Пусть x - переменная типа int. Обозначим через px указатель. Унарная операция & выдает адрес объекта, так что оператор px = &x; присваивает переменной px адрес переменной x. Говорят, что px "указывает" на x. Операция & применима только к адресным выражениям, так что конструкции вида &(x-1) и &3 незаконны. Унарная операция * называется операцией разадресации или операцией разрешения адреса. Эта операция рассматривает свой операнд как адрес и обращается по этому адресу, чтобы извлечь объект, содержащийся по этому адресу. Следовательно, если y тоже имеет тип int, то y = *px; присваивает y содержимое того, на что указывает px.

Рассмотрим функции управления памятью:

· malloc() – предназначена для выделения непрерывной области памяти заданного размера, например, void * malloc(size_t size), где size_t – тип результата операции sizeof. size – размер выделяемой памяти в байтах. Функция malloc

возвращает указатель без типа. Если выделить память не удалось, функция возвращает значение NULL..

·calloc() – предназначена для выделения памяти под заданное количество объектов, каждый из которых имеет размер size байт, всего n ´ size байт. Возвращает указатель на первый байт выделенной область памяти. Если выделить память не удалось, функция возвращает значение NULL: void * calloc(size_t n, size_t size);

·free() – предназначена для освобождения памяти,

выделенной функциями malloc(),calloc(),realloc(). После выполнения функции освобожденная память может быть выделена вновь под другие данные: void free(void* ptr); где prt – указатель на область памяти, созданной только функциями динамического управления памятью malloc(),calloc()

Уте́чка па́мяти (англ. memory leak) — процесс неконтролируемого уменьшения объёма свободной оперативной памяти (RAM) компьютера, связанный с ошибками в работающих программах, вовремя не освобождающих ненужные уже участки памяти, или с ошибками системных служб контроля памяти. Утечки памяти приводят к тому, что потребление памяти программой неконтролируемо возрастает, в результате рано или поздно вступают в действие архитектурные ограничения среды исполнения (операционной системы, виртуальной машины, ЭВМ), и тогда новое выделение памяти становится невозможным. В этой ситуации в программе, которая запрашивает память, обычно происходит аварийная остановка. Это может по стечению обстоятельств произойти и совсем с другой программой после того, как программа, подверженная утечкам, потребит всю память ЭВМ. Некоторые языки программирования (например, Оберон, Java, языки платформы .NET) предоставляют средства, позволяющие автоматически освобождать неиспользуемую память («сборщик мусора», англ. garbage collector). Сборщики мусора решают также и проблему циклических ссылок, но сборка мусора является ресурсоёмкой операцией. За использование подобных средств приходится расплачиваться быстродействием системы. В системе со сборкой мусора обязанность освобождения памяти от объектов, которые больше не используются, возлагается на среду исполнения программы. Программист лишь создаёт динамические объекты и пользуется ими, он может не заботиться об удалении объектов, поскольку это делает за него среда. Для осуществления сборки мусора в состав среды исполнения включается специальный программный модуль, называемый «сборщиком мусора». Этот модуль периодически запускается, определяет, какие из созданных в динамической памяти объектов более не используются, и освобождает занимаемую ими память. Периодичность запуска сборщика мусора определяется особенностями системы. Сборщик может работать в фоновом режиме, запускаясь при неактивности программы (например, когда программа простаивает, ожидая ввода данных пользователем).

7.

Шаг 1. Текст программы, называемый исходный модуль, подвергается препроцессорной обработке. Специальная программа (препроцессор) сканирует исходный текст и выполняет необходимые действия над текстом программы. Например, встретив в тексте программы директиву #include, она вместо этой директивы записывает текст того файла, который указан в директиве. В нашем примере директивой #include <iostream> в тест программы будет добавлено содержимое стандартного файла iostream.

Когда препроцессор встречает директиву #define, то он удаляет её из текста программы и далее по тексту выполняет замену, определенную этой директивой. Например, если в тексте есть оператор s = PI * r * r; и использовалась директива #define PI 3.14159265 то после обработки препроцессором получится так: s = 3.14159265 * r * r;

Шаг 2. После препроцессорной обработки получается полный исходный модуль (чистый текст), который поступает в компилятор. Программа-компилятор выполняет преобразование текста программы с языка С++ в объектный модуль, т.е. в текст на машинном языке. Процесс компиляции очень не прост. Как правило, компиляторы многократно (в несколько проходов) обрабатывают текст программы, выполняя те или иные действия.

Шаг 3. Объектный модуль, полученный в результате компиляции, хотя и представляет собой файл в машинных кодах, но совершенно не пригоден для исполнения компьютером. Почему? Чего ещё не хватает? Всякая программа на С++ использует стандартную библиотеку, поставляемую вместе с компилятором. В ней хранится код большого количества стандартных функций, которые мы сами не пишем, а применяем в готовом виде. Например, математические функции, операции ввода-вывода и многое другое. Поэтому на данном шаге компоновщик (или подругому, редактор связей, линкер) к объектному модулю добавляет машинный код всех тех стандартных функций, которые использованы в программе, и прописывает их адреса для вызова. Заметьте себе, не вообще всех, а только необходимых. Кроме того, программа может состоять не из одной функции main(), а содержать ещё множество собственных функций программиста. Каждая из них компилируется по-отдельности, к каждой из них подключаются необходимые стандартные функции, и все они добавляются к выходному файлу. В итоге компоновщик собирает из одного или нескольких объектных модулей плюс необходимых функций из стандартной библиотеки исполняемый модуль, пригодный для выполнения компьютером. Именно исполняемый модуль (исполняемая программа) запускается на исполнение.

8.

В языках программирования Си и C++, заголовочные файлы

основной способ подключить к программе типы данных, структуры, прототипы функций, перечислимые типы, и макросы, используемые в другом модуле. Имеет по умолчанию расширение .h; иногда для заголовочных файлов языка C++ используют расширение .hpp. Чтобы избежать повторного включения одного и того же кода, используются директивы #ifndef, #define, #endif. Заголовочный файл в общем случае может содержать любые конструкции языка программирования, но на практике исполняемый код (за исключением inline-функций в C++) в заголовочные файлы не помещают. Например, идентификаторы, которые должны быть объявлены более чем в одном файле, удобно описать в заголовочном файле, а затем его подключать по мере надобности. Подобным же образом работает модульность и в большинстве ассемблеров. По сложившейся традиции, в заголовочных файлах объявляют функции стандартной библиотеки Си и Си++.

Препроцессор С/С++ — программный инструмент, изменяющий код программы для последующей компиляции и сборки, используемый в языках программирования Си и его потомка - C++. Этот препроцессор обеспечивает использование стандартного набора возможностей:

Замена триграфов ??=, ??(, ??) (и других) символами #, [, ] Замена комментариев пустыми строками

Включение файла — #include Макроподстановки — #define

Условная компиляция — #if, #ifdef, #elif, #else, #endif

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

Директива #include включает в текст программы содержимое указанного файла. Эта директива имеет две формы: #include "имя файла", #include <имя файла>. Директива #include широко используется для включения в программу так называемых заголовочных файлов, содержащих прототипы библиотечных функций, и поэтому большинство программ на СИ начинаются с этой директивы.

Директива #define служит для замены часто использующихся констант, ключевых слов, операторов или выражений некоторыми идентификаторами. Идентификаторы, заменяющие текстовые или числовые константы, называют именованными константами. Идентификаторы, заменяющие фрагменты программ, называют макроопределениями, причем макроопределения могут иметь аргументы. Директива #define имеет две синтаксические формы: #define идентификатор текст

#define идентификатор (список параметров) текст Эта директива заменяет все последующие вхождения идентификатора на текст. Такой процесс называется

макроподстановкой. Текст может представлять собой любой фрагмент программы на СИ

Директивы #if, #ifdef, #ifndef, #else, #elif и #endif

используются для избирательной компиляции различных фрагментов программы. Главная идея состоит в том, что если выражение, стоящее после директив #if, #ifdef и #ifndef, оказывается истинным, то будет скомпилирован код, расположенный между одной из этих трех директив и директивой #endif; в противном случае данный код будет опущен. Директива #endif используется для обозначения конца блока #if. Директиву #else можно использовать с любой из перечисленных выше директив для предоставления альтернативного варианта компиляции. Общая форма записи директивы #ifdef такова. #ifdef имя_макроса

Если имя_макроса определено в операторе #define, то будет скомпилирован блок кода, следующий за оператором #ifdef.

9.

Потоки Поток - это файл или физическое устройство (т.е. принтер

или монитор, в UNIX-системах физические устройства представляются файлами), которые управляются через указатель на поток.

Существует внутренняя C-структура данных FILE, которая используется для работы с потоками и определена в stdio.h. При вводе/выводе посредством потоков необходимо использовать эту структуру FILE.

Файловая система языка С предназначена для работы с самыми разными устройствами, в том числе терминалами, дисководами и накопителями на магнитной ленте. Даже если какое-то устройство сильно отличается от других, буферизованная файловая система все равно представит его в виде логического устройства, которое называется потоком. Все потоки ведут себя похожим образом. И так как они в основном не зависят от физических устройств, то та же функция, которая выполняет запись в дисковый файл, может ту же операцию выполнять и на другом устройстве, например, на консоли. Потоки бывают двух видов: текстовые и двоичные.

Текстовые потоки Текстовый поток — это последовательность символов. В

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

возврата каретки и перевода строки. Поэтому может и не быть однозначного соответствия между символами, которые пишутся (читаются), и теми, которые хранятся во внешнем устройстве. Кроме того, количество тех символов, которые пишутся (читаются), и тех, которые хранятся во внешнем устройстве, может также не совпадать из-за возможных преобразований.

Двоичные потоки Двоичный поток — это последовательность байтов, которая

взаимно однозначно соответствует байтам на внешнем устройстве, причем никакого преобразования символов не происходит. Кроме того, количество тех байтов, которые

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

Файлы

Вязыке С файлом может быть все что угодно, начиная с дискового файла и заканчивая терминалом или принтером. Поток связывают с определенным файлом, выполняя операцию открытия. Как только файл открыт, можно проводить обмен информацией между ним и программой. Но не у всех файлов одинаковые возможности. Например, к дисковому файлу прямой доступ возможен, в то время как к некоторым принтерам — нет. Таким образом, мы пришли к одному важному принципу, относящемуся к системе ввода/вывода языка С: все потоки одинаковы, а файлы — нет.

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

Файл отсоединяется от определенного потока (т.е. разрывается связь между файлом и потоком) с помощью операции закрытия. При закрытии файла, открытого с целью вывода, содержимое (если оно есть) связанного с ним потока записывается на внешнее устройство. Этот процесс, который обычно называют дозаписью[1] потока, гарантирует, что никакая информация случайно не останется в буфере диска. Если программа завершает работу нормально, т.е. либо main() возвращает управление операционной системе, либо вызывается exit(), то все файлы закрываются автоматически. В случае аварийного завершения работы программы, например, в случае краха или завершения путем вызова abort(), файлы не закрываются.

У каждого потока, связанного с файлом, имеется управляющая структура, содержащая информацию о файле; она имеет тип FILE. В этом блоке управления файлом[2] никогда ничего не меняйте[3].

Если вы новичок в программировании, то разграничение потоков и файлов может показаться излишним или даже "заумным". Однако надо помнить, что основная цель такого разграничения — это обеспечить единый интерфейс. Для выполнения всех операций ввода/вывода следует использовать только понятия потоков и применять всего лишь одну файловую систему. Ввод или вывод от каждого устройства автоматически преобразуется системой ввода/вывода в легко управляемый поток.

Потоковый I/O -- буферизированный: Это означает, что блок данных фиксировнного размера читаентся/пишется в файл не непосредственно, а через временную область хранения (буфер).

Это повышает эффективность I/O, но будьте осторожны: данные, записанные в буфер, не появяться в файле или на устройстве, пока буфер не будет очищен. Всякое неуспешное завершение программы может вызвать проблемы. Потоковый I/O эффективен для символов и строк. Небуферизированный ввод-вывод Этот тип ввода-вывода позволяет избавиться от

генерирования дополнительных данных, что свойственно для буферизированного ввода-вывода (операций копирования данных и выделение/освобождение буфера) и прямого ввода-вывода (создания и уничтожения списка дескрипторов памяти), но за это приходится расплачиваться ограниченностью применения данного типа ввода-вывода. При небуферизированном вводе-выводе драйверу непосредственно передается адрес буфера запросившего приложения. Внимательный читатель быстро догадается, что, поскольку виртуальный адрес имеет смысл только в контексте определенного процесса или потока, драйвер должен вызываться в контексте запросившего приложения. Более того, драйвер должен выполнить операцию в этом же контексте (т.е. драйвер не может поместить запрос в очереди для последующего выполнения в произвольном контексте).

10.

Что касается файловой системы языка С, то в начале выполнения программы автоматически открываются три потока. Это stdin (стандартный поток ввода), stdout (стандартный поток вывода) и stderr (стандартный поток ошибок). Обычно эти потоки направляются к консоли, но в средах, которые поддерживают перенаправление ввода/вывода, они могут быть перенаправлены операционной системой на другое устройство. (Перенаправление ввода/вывода поддерживается, например, такими операционными системами, как Windows, DOS, UNIX и OS/2.)

Так как стандартные потоки являются указателями файлов, то они могут использоваться системой ввода/вывода языка С также для выполнения операций ввода/вывода на консоль. Вообще говоря, stdin используется для считывания с консоли, a stdout и stderr — для записи на консоль.

Вроли указателей файлов потоки stdin, stdout и stderr можно применять в любой функции, где используется переменная типа FILE *. Например, для ввода строки с консоли можно написать примерно такой вызов fgets():

char str[255]; fgets(str, 80, stdin);

Ввод/вывод в C++ осуществляется с помощью потоков библиотеки C++, доступных при подключении заголовочного файла iostream.h (в VC++.NET – объекта-заголовка iostream).

Поток представляет собой объект какого-либо потокового класса.

Потоковые классы сконструированы на основе базового класса ios:

ios – базовый потоковый класс; istream – класс входных потоков; ostraem – класс выходных потоков;

iostream – класс двунаправленных потоков ввода/вывода.

Впотоковые классы включены операторы добавления данных в поток << и извлечения данных из потока >>.

На основе класса istream в библиотеке C++ объявлен объектпоток cin, представляющий собой стандартный буферизованный входной поток, связанный обычно с

клавиатурой консоли. Извлечение данных из потока имеет следующую форму записи:

int a; float b;

cin >> a >> b;

На основе класса ostream объявлен объект-поток cout, представляющий собой стандартный буферизованный выходной поток, связанный обычно с дисплеем консоли. Форма записи добавления данных в поток следующая: cout << a << b;

при этом значения переменных a и b выводятся на экран без разделителя и в формате, заданном по умолчанию. Перемещение курсора на следующую строку экрана после вывода данных не происходит.

Для перевода курсора на новую строку используется манипулятор endl:

cout << a <<" "<< b << endl;

Впотоках cin и cout можно форматировать данные, для этого используются специальные манипуляторы, доступные через заголовочный файл iomanip.h.

11.

12.

Абстракция Абстрагирование — это способ выделить набор значимых

характеристик объекта, исключая из рассмотрения незначимые. Соответственно, абстракция — это набор всех таких характеристик.[1] Инкапсуляция

Инкапсуляция — это свойство системы, позволяющее объединить данные и методы, работающие с ними в классе, и скрыть детали реализации от пользователя.[1] Наследование Наследование — это свойство системы, позволяющее

описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется базовым, родительским или суперклассом. Новый класс — потомком, наследником или производным классом.[1] Полиморфизм

Полиморфизм — это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.[1] Класс

Класс является описываемой на языке терминологии (пространства имён) исходного кода моделью ещё не существующей сущности (объекта). Фактически он описывает устройство объекта, являясь своего рода чертежом. Говорят, что объект — это экземпляр класса. При этом в некоторых исполняющих системах класс также может представляться некоторым объектом при выполнении программы посредством динамической идентификации типа данных. Обычно классы разрабатывают таким образом, чтобы их объекты соответствовали объектам предметной области. Объект Сущность в адресном пространстве вычислительной

системы, появляющаяся при создании экземпляра класса или копирования прототипа (например, после запуска результатов компиляции и связывания исходного кода на выполнение).

Прототип Прототип — это объект-образец, по образу и подобию

которого создаются другие объекты. Объекты-копии могут сохранять связь с родительским объектом, автоматически наследуя изменения в прототипе; эта особенность определяется в рамках конкретного языка.

Вцентре ООП находится понятие объекта. Объект — это сущность, которой можно посылать сообщения, и которая может на них реагировать, используя свои данные. Объект — это экземпляр класса. Данные объекта скрыты от остальной программы. Сокрытие данных называется инкапсуляцией. Наличие инкапсуляции достаточно для объектности языка программирования, но ещё не означает его объектной ориентированности — для этого требуется наличие наследования.

Но даже наличие инкапсуляции и наследования не делает язык программирования в полной мере объектным с точки зрения ООП. Основные преимущества ООП проявляются только в том случае, когда в языке программирования реализован полиморфизм; то есть возможность объектов с одинаковой спецификацией иметь различную реализацию. Как уже говорилось выше, в современных объектноориентированных языках программирования каждый объект является значением, относящимся к определённому классу. Класс представляет собой объявленный программистом составной тип данных, имеющий в составе:

Поля данных Параметры объекта (конечно, не все, а только необходимые

в программе), задающие его состояние (свойства объекта предметной области). Иногда поля данных объекта называют свойствами объекта, из-за чего возможна путаница. Физически поля представляют собой значения (переменные, константы), объявленные как принадлежащие классу.

Методы Процедуры и функции, связанные с классом. Они

определяют действия, которые можно выполнять над объектом такого типа, и которые сам объект может выполнять.

Классы могут наследоваться друг от друга. Класс-потомок получает все поля и методы класса-родителя, но может дополнять их собственными либо переопределять уже имеющиеся. Большинство языков программирования поддерживает только единичное наследование (класс может иметь только один класс-родитель), лишь в некоторых допускается множественное наследование — порождение класса от двух или более классов-родителей. Множественное наследование создаёт целый ряд проблем, как логических, так и чисто реализационных, поэтому в полном объёме его поддержка не распространена. Вместо этого в 1990-е годы появилось и стало активно вводиться в

объектно-ориентированные языки понятие интерфейса. Интерфейс — это класс без полей и без реализации, включающий только заголовки методов. Если некий класс наследует (или, как говорят, реализует) интерфейс, он должен реализовать все входящие в него методы. Использование интерфейсов предоставляет относительно дешёвую альтернативу множественному наследованию.

Взаимодействие объектов в абсолютном большинстве случаев обеспечивается вызовом ими методов друг друга. Инкапсуляция обеспечивается следующими средствами Контроль доступа Поскольку методы класса могут быть как чисто внутренними,

обеспечивающими логику функционирования объекта, так и внешними, с помощью которых взаимодействуют объекты, необходимо обеспечить скрытость первых при доступности извне вторых. Для этого в языки вводятся специальные синтаксические конструкции, явно задающие область видимости каждого члена класса. Традиционно это модификаторы public, protected и private, обозначающие, соответственно, открытые члены класса, члены класса, доступные только из классов-потомков и скрытые, доступные только внутри класса. Конкретная номенклатура модификаторов и их точный смысл различаются в разных языках.

Методы доступа Поля класса, в общем случае, не должны быть доступны

извне, поскольку такой доступ позволил бы произвольным образом менять внутреннее состояние объектов. Поэтому поля обычно объявляются скрытыми (либо язык в принципе не позволяет обращаться к полям класса извне), а для доступа к находящимся в полях данным используются специальные методы, называемые методами доступа. Такие методы либо возвращают значение того или иного поля, либо производят запись в это поле нового значения. При записи метод доступа может проконтролировать допустимость записываемого значения и, при необходимости, произвести другие манипуляции с данными объекта, чтобы они остались корректными (внутренне согласованными). Методы доступа называют ещё аксессорами (от англ. access — доступ), а по отдельности — геттерами (англ. get — чтение) и сеттерами (англ. set — запись)[6].

ООП ориентировано на разработку крупных программных комплексов, разрабатываемых командой программистов (возможно, достаточно большой). Проектирование системы

вцелом, создание отдельных компонентов и их объединение в конечный продукт при этом часто выполняется разными людьми, и нет ни одного специалиста, который знал бы о проекте всё.

Объектно-ориентированное проектирование состоит в описании структуры и поведения проектируемой системы, то есть, фактически, в ответе на два основных вопроса:

Из каких частей состоит система.

В чём состоит ответственность каждой из частей.

13.

Инкапсуляция Инкапсуляция есть объединение в единое целое данных и

алгоритмов обработки этих данных. В рамках ООП данные называются полями объекта, а алгоритмы - объектными методами.

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

Другим немаловажным следствием инкапсуляции является легкость обмена объектами, переноса их из одной программы в другую. Можно сказать, что ООП «провоцирует» разработку библиотек объектов, таких как

Turbo Vision.

Наследование Наследование есть свойство объектов порождать своих

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

Принцип наследования решает проблему модификации свойств объекта и придает ООП в целом исключительную гибкость. При работе с объектами программист обычно подбирает объект, наиболее близкий по своим свойствам для решения конкретной задачи, и создает одного или нескольких потомков от него, которые «умеют» делать то, что не реализовано в родителе.

Последовательное проведение в жизнь принципа «наследуй и изменяй» хорошо согласуется с поэтапным подходом к разработке крупных программных проектов и во многом стимулирует такой подход.

Полиморфизм Полиморфизм - это свойство родственных объектов (т.е.

объектов, имеющих одного общего родителя) решать схожие по смыслу проблемы разными способами. В рамках ООП поведенческие свойства объекта определяются набором входящих в него методов. Изменяя алгоритм того или иного метода в потомках объекта, программист может придавать этим потомкам отсутствующие у родителя специфические свойства. Для изменения метода необходимо перекрыть его

впотомке, т.е. объявить в потомке одноименный метод и реализовать в нем нужные действия. В результате в объектеродителе и объекте-потомке будут действовать два одноименных метода, имеющие разную алгоритмическую основу и, следовательно, придающие объектам разные свойства. Это и называется полиморфизмом объектов.

14.

Наиболее важный аспект разработки программного обеспечения — ясно понимать, что именно вы пытаетесь построить. — Бьярн Страуструп (Bjarne Stroustrup.

В С++ члены класса классифицируются в соответствии с правами доступа на следующие три кате­гории: публичные

(public), частные (private) и защищенные (protected). Любая функция програм­мы имеет доступ к публичным членам. Доступ к частному члену имеют только функции-члены класса или функции-друзья класса. Защищенные члены аналогичны частным членам. Разница между ними появляется только при наследовании классов.

Когда один класс наследует другой, все публичные члены базового класса становятся публич­ными членами производного класса. В противоположность этому частные члены базового класса не доступны внутри производного класса. Защищен­ный член подобен частному, за

исключением механизма наследования. При наследовании защи­щенного члена производный класс также имеет к нему доступ. Таким образом, указав специфи­катор доступа protected, можно позволить использовать член внутри иерархии классов и запретить доступ к нему извне этой иерархии.

Всписке членов класса можно объявлять переменные, функции, классы, перечисления, а также дружественные функции и классы. Член класса не может объявляться в списке членов класса дважды. Это относиться и к функциям (хотя могут быть функции с одним именем, но разным набором формальных параметров). Кроме того, нельзя объявить в классе переменную и функцию с одним именем. Список членов класса определяет полный набор членов этого класса. Нельзя добавлять к классу члены ещё в какомто месте. Член класса не может иметь инициализатора. Член класса не может быть объявлен со спецификациями класса памяти auto, extern и register. Инициализация объектов класса осуществляется с помощью конструкторов.

Член класса может быть:

приватным (private) – это значит, что его имя может употребляться лишь внутри функций-членов класса и друзей класса, в котором этот член класса объявлен;

защищённым (protected) – это значит, что его имя может употребляться лишь внутри функций-членов класса, друзей этого класса и производных от него классов;

публичным (public) – это значит, что его имя может употребляться внутри любой функции (а также и вне функций в инициализаторах).

Функция, объявленная в классе без спецификатора friend, называется функцией-членом класса. Её вызов имеет соответствующий синтаксис. Описание функции-члена класса относиться к области действия класса. Это означает, что функция-член класса может непосредственно использовать имена членов своего класса.

Вобъявлении функции после списка параметров можно добавить модификатор const. Это будет означать, что функция не меняет состояние объекта, к которому она применяется. Суффикс const является частью типа функции и должен записываться, когда функция определяется вне класса. Константную функцию-член класса можно вызвать как для константного, так и для неконстантного объекта, в то время как неконстантную функцию можно вызвать только для объекта, не являющегося константой.

15.

Конструктор предназначен для инициализации объекта. Он вызывается автоматически при создании объекта класса Имя конструктора совпадает с именем класса. Ниже перечислены свойства конструкторов.

Конструктор не возвращает значение, даже типа void. Класс может иметь несколько конструкторов с разными параметрами для разных видов инициализации.

Если программист не указал ни одного конструктора или какие-то поля не были инициализированы, полям значимых типов присваивается нуль, полям ссылочных типов — значение null.

Конструктор, вызываемый без параметров, называется конструктором по умолчанию.

Конструктор по умолчанию (англ. default constructor), в объектно-ориентированных языках программирования — конструктор, который может быть вызван без аргументов.

ВC++ и Java если нет явным образом опредёленных конструкторов в классе, то компилятор использует конструктор по умолчанию, опредёленный неявным способом, который аналогичен «чистому»[уточнить] конструктору по умолчанию. Поэтому, класс не гарантирует наличия конструктора по умолчанию (то есть когда программист явным образом определяет только конструктор, который не по умолчанию). Некоторые программисты явным образом задают конструктор по умолчанию по привычке, чтобы не забыть в дальнейшем, но это не обязательно. В C++ только массивы имеют конструкторы по умолчанию, которые создают каждый элемент при помощи конструктора по умолчанию для их типа.

Конструктором копирования (в англоязычной литературе используется термин copy constructor) называется специальный конструктор в языке программирования C++, применяемый для создания нового объекта как копии уже существующего. Такой конструктор принимает как минимум один аргумент: ссылку на копируемый объект.

Обычно компилятор автоматически создает конструктор копирования для каждого класса (известные как неявные конструкторы копирования, т.е. конструкторы копирования, заданные неявным образом), но в некоторых случаях программист создает конструктор копирования, называемый в таком случае явным конструктором копирования (или "конструктором копирования, заданным явным образом"). В подобных случаях компилятор не создает неявные конструкторы.

Конструктор копирования в основном необходим когда объект имеет указатель или неразделяемую ссылку, как например, на файл, в этом случае вам обычно также потребуется деструктор и оператор присваивания (см. Правило трех).

Дестру́ктор — специальный метод класса, служащий для деинициализации объекта (например освобождения памяти). Деструктор автоматически запускается каждый раз, когда программа уничтожает объект.

16.

Одной из привлекательных особенностей языкаC++ является возможность распространения действия стандартных операций на операнды, для которых эти операции первоначально в языке не предполагались. Например, еслиS1 иS2 - символьные строки, то их соединение(конкатенацию) удобно было бы обозначить какS1+S2. Однако бинарная операция+ в языкеC++ предназначена для арифметических операндов и не предусматривает строковых операндов. Но еслиS1 иS2 определить как объекты некоторого класса, то для них можно ввести операцию+, выполняемую по правилам, которые выбрал программист. Для этих целей языкC++ позволяет распространить действие любой стандартной операции на новые типы данных, вводимые пользователем. Распространить операцию на новые типы данных позволяет механизм перегрузки стандартных операций. Составители: Попов А.С., Еремин М.А. 4 При перегрузке операций нельзя вводить новые лексические обозначения операций, даже формируя их из допустимых

символов. Все операцииC++ могут быть перегружены, кроме операций точка(.), разыменование(*), разрешение области действия(::), условная(?:) и sizeof, #, ##, директивы препроцессора и некоторые другие. Операции =, [], (),-> и ->* могут быть перегружены только как нестатические функциикомпоненты. Они не могут быть перегружены для перечислимых типов. Все остальные операции можно перегружать, чтобы применять их к каким-то новым типам объектов, вводимым пользователем. Кроме того, многие операции уже перегружены вC++. Например, арифметические операции применяются к разным типам данных– целым числам, действительным и т.д., именно в результате того, что они перегружены. тип имя_класса::operator#(список_аргументов) Чтобы перегрузить стандартные операции для АТД, необходимо специальным образом их определить для объектов данного АТД. Это возможно, если хотя бы один из операндов является объектом некоторого класса, то есть введенного пользователем типа. Переопределять операции для стандартных типов данных нельзя. Для распространения действия операции на новые данные Объектноориентированные языки программирования. Практикум. 5 программист определяет специальную функцию, называемую«операция-функция», «функция-операция» или«операторная функция» (мы приводим несколько вариантов перевода термина«operator function» на русский язык). Формат ее определения: тип_возращаемого_значения operator знак_операции (спецификация_параметров_операции-функции)

{

операторы_тела_операции-функции

}

При необходимости может добавляться и прототип операции-функции. Например, для распространения действия бинарной операции* на объекты класса Т может быть введена функция с заголовком: T operator*(Tx, Ty) Количество параметров у операции-функции зависит от арности операции и от способа определения функции. Чтобы явная связь с классом была обеспечена, операция-функция должна быть либо компонентом класса, либо она должна быть определена в классе как дружественная, либо у нее должен быть хотя бы один параметр типа класс(или ссылка на класс). Рассмотрим иллюстрацию перегрузки

{

// действия, определенные применительно к классу

}

Функции-друзья — это функции, не являющиеся функциямичленами и тем не менее имеющие доступ к защищённым и закрытым членам класса. Они должны быть объявлены в теле класса как friend. Дружественной может быть объявлен как весь класс, так и функция-член класса. Если класс A — объявлен в классе B как друг, то все собственные (не унаследованные) функции-члены класса A могут обращаться к любым членам класса B. Четыре важных ограничения, накладываемых на отношения дружественности в C++:

Дружественность не транзитивна. Если A объявляет другом B, а B, в свою очередь, объявляет другом C, то C не становится автоматически другом для A. Для этого A должен явно объявить C своим другом.

Дружественность не взаимна. Если класс A

объявляет другом класс B, то он не становится автоматически другом для B. Для этого должно существовать явное объявление дружественности A в классе B.

Дружественность не наследуется. Если A объявляет класс B своим другом, то потомки B не становятся автоматически друзьями A. Для этого каждый из них должен быть объявлен другом A в явной форме.

Дружественность не распространяется на потомков. Если класс A объявляет B другом, то B не становится автоматически другом для классовпотомков A. Каждый потомок, если это нужно, должен объявить B своим другом самостоятельно.

В общем виде это правило можно сформулировать следующим образом: «Отношение дружественности существует только между теми классами (классом и функцией), для которых оно явно объявлено в коде, и действует только в том направлении, в котором оно объявлено».

17.

ключевое слово this обозначает указатель на объект, для которого вызвана данная функция, т.е. внутри функциичлена класса член того же класса с именем х можно обозначать как x, и как this -> x. Указатель на объект, для которого вызвана функция, является неявным параметром этой функции.

class X

{ private: int a; public:

void f(int a) { this -> a = a; }

};

// Члену класса a присваивается значение

параметра a

 

Указатель this в функции-члене класса Х имеет тип X * const. Однако, это не обычная переменная, невозможно получить её адрес или присвоить ей что-нибудь. В константной функции-члене класса Х this имеет тип const X * const для предотвращения модификации самого объекта.

В большинстве случаев использование this является неявным. В частности, каждое обращение к нестатическому члену класса неявно использует this для доступа к члену соответствующего объекта.

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

class X

{...

public: X& f();

};

X& X::f() { ...

return *this;

}

nline-функция — это такая функция, чье тело подставляется в

каждую точ­ку вызова, вместо того, чтобы генерировать код вызова. Имеются два способа создания inlineфункции. Первый заключается в использовании

модификатора inline. Например, для создания inline-функции f, возвращающей значение типа int и не имеющей параметров, достаточно объявить ее следующим образом:

inline int f()

{

// ...

}

Общая форма объявления inline-функции следующая:

inline объявление_функции

Модификатор inline предшествует всем частям объявления функции.

Всякий раз, когда вызывается функция, необходимо выполнить серию инструкций для формирования вызова функции, вставки аргументов в стек и возврата значения из функции. В некоторых случаях для этого приходится использовать много тактов центрального процессора. При использовании inline-функции нет необходимости в таких дополнительных действиях и скорость выполнения программы возрастает.

Однако в тех случаях, когда размер inlineфункции достаточно большой, общий объем программы также возрастает. Поэтому в качестве inline-функции обычно используются очень маленькие функции. Большие функции реализуются обычным способом.

Члены класса могут использоваться с ключевым словом static. В данном контексте его значение сходно с тем, которое оно имеет в С. Когда член класса объявляется как статический, то тем самым компилятору дается указание, что должна существовать только одна копия этого члена, сколько бы объектов этого класса ни создавалось. Статический член используется совместно всеми объектами данного класса. Все статические данные инициализируются нулями при создании первого объекта, и другая инициализация не предусмотрена. 1) При объявлении члена класса (функции или переменной) ключевое слово static означает, что этот член класса является статическим. К статическим членам класса разрешен доступ вида: T::member. Статические фуннкции-члены класса не имеют неявного формального параметра this, не могут быть виртуальными или иметь модификатор const. Статические переменные не являются частью объектов данного класса, т.е. являются "общими" для всех объектов данного класса. Понятие статической переменной класса ближе всего к глобальным переменным C/C++ с точностью до правил доступа. В объектно ориентированной терминологии нестатические члены класса принято называть членами объекта (instance member), а статические — членами класса (class member). 2) При объявлении локальных переменных функции ключевое слово static означает, что переменная имеет статический класс памяти (static storage duration), т.е.

существует в течение всего времени выполнения программы, в отличие от переменных с локальным классом памяти (automatic storage duration).

18.

Наследование — механизм языка позволяющий написать новый класс на основе уже существующего(родительского, базового) класса. Класс–потомок может добавить собственные методы и свойства и пользоваться родительскими методами и свойствами. Позволяет строить иерархии классов.

Класс, от которого произошло наследование, называется базовым или родительским (англ. base class). Классы, которые произошли от базового, называются потомками, наследниками или производными классами (англ. derived class).

В некоторых языках используются абстрактные классы. Абстрактный класс — это класс, содержащий хотя бы один абстрактный метод, он описан в программе, имеет поля, методы и не может использоваться для непосредственного создания объекта. То есть от абстрактного класса можно только наследовать. Объекты создаются только на основе производных классов, наследованных от абстрактного. Например, абстрактным классом может быть базовый класс «сотрудник вуза», от которого наследуются классы «аспирант», «профессор» и т. д. Так как производные классы имеют общие поля и функции (например, поле «год рождения»), то эти члены класса могут быть описаны в базовом классе. В программе создаются объекты на основе классов «аспирант», «профессор», но нет смысла создавать объект на основе класса «сотрудник вуза». class A{ //базовый класс

};

class B : public A{ //public наследование };

class C : protected A{ //protected наследование };

class Z : private A{ //private наследование };

В C++ существует три типа наследования: public, protected, private. Спецификаторы доступа членов базового класса меняются в потомках следующим образом:

Если класс объявлен как базовый для другого класса со спецификатором доступа public, тогда public члены базового класса доступны как public члены производного класса, protected члены базового класса доступны как protected члены производного класса.

Если класс объявлен как базовый для другого класса со спецификатором доступа protected, тогда public и protected члены базового класса доступны как protected члены производного класса.

Если класс объявлен как базовый для другого класса со спецификатором доступа private, тогда public и protected члены базового класса доступны как private члены производного класса.

используя protected наследование, программист предполагает, что внутри всех потомков и потомков потомков и потомков потомков потомков будут использоваться только такие элементы, передаваемые механизмом наследования, которые будут защищены от внешнего воздействия извне своих классов.

19.

Режимы доступа: внешнее, защищенное и внутреннее наследование:

Вспомним описание разделов класса

сlass имеет разделы с различным режимом доступа: public – доступный, открытый раздел, реализующий интерфейс класса, т.е. способы работы с ним.

private – внутренний, закрытый раздел класса, недоступный снаружи (недоступен и в порожденном классе тоже). protected – защищеный раздел класса, недоступный снаружи, доступен только в порожденном классе при наследованиии.

Важно:

protected используется при проектировании базовых классов

виерархии. Т.е. классы разрабатываются в предположении возможного наследования.

Эти же режимы доступа - public, private и protected -

используются при описании заголовка наследуемого класса перед именем базового класса:

class <наследник>: {public | private | protected} <предок>{ //

задается один из режимов

};

Режим доступа public – внешнее наследование - интерфейс базового класса (раздел public) становится внешним интерфейсом производного класса (применяется чаще всего).

Режим доступа protected – защищенное наследование - внешний и защищенный разделы базового класса становится защищеными разделами производного класса, т.е. доступны только при следующем наследовании.

Режим доступа private – внутреннее наследование - внешний и защищенный разделы базового класса становится внутренними разделами производного класса, недоступны снаружи.

Помним, что наследование применяется для построения иерархии классов.

Иерархия классов может быть основой системы (так Visual Component Library (VCL) является основой систем разработки программ Delphi и Builder)

При необходимости в производном классе можно перекрыть (переопределить) свойства и методы базового класса.

В C++ производный класс может быть порождён из любого числа непосредственных базовых классов. Наличие у производного класса более чем одного непосредственного базового класса называется множественным наследием. Синтаксически множественное наследование отличается от единичного наследования списком баз, состоящим более чем из одного элемента.

class A

{

}; class B

{

};

class C : public A, public B

{

};

При создании объектов-представителей производного класса, порядок расположения непосредственных базовых классов в списке баз определяет очерёдность вызова конструкторов умолчания.

Этот порядок влияет и на очерёдность вызова деструкторов при уничтожении этих объектов.

внаборе непосредственных базовых классов, которые участвуют в формировании производного класса не должно встречаться повторяющихся элементов. Вместе с тем, один и тот же класс может участвовать в формировании нескольких (а может быть и всех) непосредственных базовых классов данного производного класса.

20.

Виртуальный метод (виртуальная функция) — в объектноориентированном программировании метод (функция) класса, который может быть переопределён в классахнаследниках так, что конкретная реализация метода для вызова будет определяться во время исполнения. Таким образом, программисту необязательно знать точный тип объекта для работы с ним через виртуальные методы: достаточно лишь знать, что объект принадлежит классу или наследнику класса, в котором метод объявлен. Виртуальные методы — один из важнейших приёмов

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

Базовый класс может и не предоставлять реализации виртуального метода, а только декларировать его существование. Такие методы без реализации называются «чистыми виртуальными» (перевод англ. pure virtual) или абстрактными. Класс, содержащий хотя бы один такой метод, тоже будет абстрактным. Объект такого класса создать нельзя (в некоторых языках допускается, но вызов абстрактного метода приведёт к ошибке). Наследники абстрактного класса должны предоставить реализацию для всех его абстрактных методов, иначе они, в свою очередь, будут абстрактными классами.

Для каждого класса, имеющего хотя бы один виртуальный метод, создаётся таблица виртуальных методов. Каждый объект хранит указатель на таблицу своего класса. Для вызова виртуального метода используется такой механизм: из объекта берётся указатель на соответствующую таблицу виртуальных методов, а из неё, по фиксированному смещению, — указатель на реализацию метода, используемого для данного класса. При использовании множественного наследования или интерфейсов ситуация несколько усложняется за счёт того, что таблица виртуальных методов становится нелинейной.

Чтобы объявить функцию как виртуальную, необходимо добавить ключевое слово virutal перед именем возвращаемого типа:

class Base

{

public:

virtual void Method ()

{

cout << "Базовый класс\n";

}

};

Чтобы сделать виртуальную функцию чистой (pure), нужно добавить после заголовка функции символы =0 (знак равенства и ноль):

class Base

{

public:

virtual void method () =0; virtual ~Base() =0;

};

В данном случае уже не нужно писать определение такой функции. Помимо этого теперь нельзя создавать объекты класса Base, так как он стал абстрактным.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]