ВІКІСТОРІНКА
Навигация:
Інформатика
Історія
Автоматизація
Адміністрування
Антропологія
Архітектура
Біологія
Будівництво
Бухгалтерія
Військова наука
Виробництво
Географія
Геологія
Господарство
Демографія
Екологія
Економіка
Електроніка
Енергетика
Журналістика
Кінематографія
Комп'ютеризація
Креслення
Кулінарія
Культура
Культура
Лінгвістика
Література
Лексикологія
Логіка
Маркетинг
Математика
Медицина
Менеджмент
Металургія
Метрологія
Мистецтво
Музика
Наукознавство
Освіта
Охорона Праці
Підприємництво
Педагогіка
Поліграфія
Право
Приладобудування
Програмування
Психологія
Радіозв'язок
Релігія
Риторика
Соціологія
Спорт
Стандартизація
Статистика
Технології
Торгівля
Транспорт
Фізіологія
Фізика
Філософія
Фінанси
Фармакологія


Процедуры ввода-вывода. Потоковый ввод-вывод.

Алгоритм

– описание последовательности действий (план), строгое исполнение которых приводит к решению поставленной задачи за конечное число шагов. Свойства алгоритмов:

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

б. Детерминированность (любое действие должно быть строго и недвусмысленно определено в каждом случае);

в. Конечность (каждое действие и алгоритм в целом должны иметь возможность завершения);

г. Массовость (один и тот же алгоритм можно использовать с разными исходными данными);

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

Дискретность - процесс решения задачи должен быть разбит на последовательность отдельных шагов, каждый из которых называется командой.

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

При наличии ошибок в алгоритме последнее сформулированное свойство может иногда нарушаться.

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

• Понятность - каждая команда алгоритма должна быть понятна тому, кто исполняет алгоритм.

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

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

• Массовость - алгоритм имеет смысл разрабатывать только в том случае, когда он будет применяться многократно для различных наборов исходных данных. Например, если составляется алгоритм обработки текстов, то вряд ли целесообразно ограничивать его возможности только русскими буквами — стоит предусмотреть также латинский алфавит, цифры, знаки препинания и т.п. Тем более что такое обобщение особых трудностей не вызывает.

Требования

Для гарантии совместной работы различные компоненты библиотеки должны удовлетворять некоторым основным требованиям. Требования должны быть общими, насколько это возможно, так что вместо высказывания "класс X должен определить функцию-член operator++()", мы говорим "для любого объекта x класса X определён ++x ". (Не определено, является ли оператор членом или глобальной функцией.) Требования установлены в терминах чётких выражений, которые определяют допустимые условия типов, удовлетворяющих требованиям. Для каждого набора требований имеется таблица, которая определяет начальный набор допустимых выражений и их семантику. Любой обобщённый алгоритм, который использует требования, должен быть написан в терминах допустимых выражений для своих формальных параметров.

2.

Этапы решения задач на ЭВМ.

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

1. Постановка задачи:

• сбор информации о задаче;

• формулировка условия задачи;

• определение конечных целей решения задачи;

• определение формы выдачи результатов;

• описание данных (их типов, диапазонов величин, структуры и т. п.).

2. Анализ и исследование задачи, модели:

• анализ существующих аналогов;

• анализ технических и программных средств;

• разработка математической модели;

• разработка структур данных.

3. Разработка алгоритма:

• выбор метода проектирования алгоритма;

• выбор формы записи алгоритма (блок-схемы, псевдокод и др.);

• выбор тестов и метода тестирования;

• проектирование алгоритма.

4. Программирование:

• выбор языка программирования;

• уточнение способов организации данных;

• запись алгоритма на выбранном языке

программирования.

5. Тестирование и отладка:

• синтаксическая отладка;

• отладка семантики и логической структуры;

• тестовые расчеты и анализ результатов тестирования;

• совершенствование программы.

6. Анализ результатов решения задачи и уточнение в случае необходимости математической модели с повторным выполнением этапов 2-5.

7. Сопровождение программы:

• доработка программы для решения конкретных задач;

• составление документации к решенной задаче, к математической модели, к алгоритму, к программе, к набору тестов, к использованию.

4. Структу́рное программи́рование

— методология разработки программного обеспечения, в основе которой лежит представление программы в виде иерархической структуры блоков. Предложена в 70-х годах XX века Э. Дейкстрой, разработана и дополнена Н. Виртом.

В соответствии с данной методологией

Любая программа представляет собой структуру, построенную из трёх типов базовых конструкций:

последовательное исполнение — однократное выполнение операций в том порядке, в котором они записаны в тексте программы;

ветвление — однократное выполнение одной из двух или более операций, в зависимости от выполнения некоторого заданного условия;

цикл — многократное исполнение одной и той же операции до тех пор, пока выполняется некоторое заданное условие (условие продолжения цикла).

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

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

Разработка программы ведётся пошагово, методом «сверху вниз».

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

5. Типы данных в языке с++

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

char (символьный);

short (короткий целый);

int (целый);

long (длинный целый);

float (вещественный);

double (вещественный с удвоенной точностью);

void (отсутствие значения) (будет рассматриваться при изучении функций в С++);

В таблице 2.1 приведены основные типы данных с диапазоном значений и назначением типов.

Константы

Константы в С++ предназначены для хранения данных, которые специально или случайно нельзя изменить. Константами могут быть такие значения как число пи (3,14…) или может быть количество дней в неделю (7), а может быть расстояние от Земли до Солнца и др. Суть констант заключается в том, что их можно использовать в вычислениях, при выводе данных, но саму константу перезаписать как переменную нельзя. Пример объявления констант:

const double pi=3.14;

const int count_day_week=7;

Если вы попытаетесь выполнить следующее действие pi = 90.05 , то компилятор выдаст ошибку.

Типизированные указатели.

Указатели

Когда компилятор обрабатывает оператор определения переменной, например, int i=10;, он выделяет память в соответствии с типом (int) и инициализирует ее указанным значением (10). Все обращения в программе к переменной по ее имени (i) заменяются компилятором на адрес области памяти, в которой хранится значение переменной. Программист может определить собственные переменные для хранения адресов областей памяти. Такие переменные называются указателями.

Итак, указатели предназначены для хранения адресов областей памяти. В C++ различают три вида указателей:

указатели на объект,

указатели на функцию

указатели на void,

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

Указатель на функцию

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

тип (*имя) ( список_типов_аргументов );

Например, объявление:

int (*fun) (double, double);

задает указатель с именем fun на функцию, возвращающую значение типа int и имеющую два аргумента типа double.

Указатель на объект

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

тип *имя;

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

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

int *a, b, *c;

описываются два указателя на целое с именами а и с, а также целая переменная b.

Размер указателя зависит от модели памяти. Можно определить указатель на указатель и т. д.

Указатель на void

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

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

Примеры объявления указателей

Указатель может быть константой или переменной, а также указывать на константу или переменную. Рассмотрим примеры:

int i; // целая переменная

const int ci = 1; // целая константа

int * pi; // указатель на целую переменную

const int * pci; // указатель на целую константу

int * const ср = &i; // указатель-константа на целую переменную

const int * const срс = &ci; // указатель-константа на целую константу

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

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

Инициализация указателей

Указатели чаще всего используют при работе с динамической памятью, называемой некоторыми эстетами кучей (перевод с английского языка слова heap). Это свободная память, в которой можно во время выполнения программы выделять место в соответствии с потребностями. Доступ к выделенным участкам динамической памяти, называемым динамическими переменными, производится только через указатели. Время жизни динамических переменных — от точки создания до конца программы или до явного освобождения памяти. В C++ используется два способа работы с динамической памятью. Первый использует семейство функций malloc и достался в наследство от С, второй использует операции new и delete.

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

Операции с указателями в C++

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

разадресация, или косвенное обращение к объекту (*)

присваивание,

сложение с константой,

вычитание,

инкремент (++),

декремент (—),

сравнение,

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

При работе с указателями часто используется операция получения адреса (&).

Ссылочный тип.

Ссылки в C++

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

тип & имя;

где тип — это тип величины, на которую указывает ссылка, & — оператор ссылки, означающий, что следующее за ним имя является именем переменной ссылочного типа, например:

int коl;

int& pal = kol; // ссылка pal - альтернативное имя для коl

const char& CR = '\n '; // ссылка на константу

Запомните следующие правила.

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

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

Тип ссылки должен совпадать с типом величины, на которую она ссылается.

Не разрешается определять указатели на ссылки, создавать массивы ссылок и ссылки на ссылки.

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

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

Перечислимый тип.

Перечисляемый тип (сокращённо перечисле́ние, англ. enumeration, enumerated type) — в программировании тип данных, чьё множество значений представляет собой ограниченный список идентификаторов.

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

type Cardsuit = (clubs, diamonds, hearts, spades);

Здесь производится объявление типа данных Cardsuit (карточная масть), значениями которого может быть любая из четырёх перечисленных констант. Переменная типа Cardsuit может принимать одно из значений clubs, diamonds, hearts, spades, допускается сравнение значений типа перечисление на равенство или неравенство, а также использование их в операторах выбора (в Паскале — case) в качестве значений, идентифицирующих варианты.

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

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

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

C++

Перечисления в языке C++ прямо наследуют поведение перечислений языка C, за исключением того, что перечисляемый тип в C++ — настоящий тип, и ключевое слово enum используется только при объявлении такого типа. Если при обработке параметра являющегося перечислением, какое-либо значение из перечисления не обрабатывается (например один из элементов перечисления забыли обработать в конструкции switch), то компилятор может выдать предупреждение о забытом значении.

Перечисления (enum)

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

enum [ имя_типа ] { список_констант };

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

enum Err {ERR__READ, ERR__WRITE, ERR_CONVERT};

Err error;

switch (error){

case ERR_READ: /* операторы */ break;

case ERR_WRITE: /* операторы */ break;

case ERR_CONVERT: /* операторы */ break;

}

Константам ERR_READ, ERR_WRITE, ERR_CONVERT присваиваются значения 0, 1 и 2 соответственно.

Другой пример:

enum {two = 2, three, four, ten = 10, eleven, fifty = ten + 40};

Константам three и four присваиваются значения 3 и 4, константе eleven — 11.

Имена перечисляемых констант должны быть уникальными, а значения могут совпадать. Преимущество применения перечисления перед описанием именованных констант и директивой #define состоит в том, что связанные константы нагляднее; кроме того, компилятор при инициализации констант может выполнять проверку типов.

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

Примечание

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

9.Структура программы на языке С++.

Структура программы

 

Программа на языке С++ состоит из функций, описаний и директив препроцессора. Одна из функций должна иметь имя main. Выполнение программы начинается с первого оператора этой функции. Простейшее определение функции имеет следующий формат:1

3 тип возвращаемого значения имя ([ параметры ]){

операторы, составляющие тело функции

}

 

 

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

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

тело функции является блоком и, следовательно, заключается в фигурные скобки;

функции не могут быть вложенными;

каждый оператор заканчивается точкой с запятой (кроме составного оператора).

 

Пример структуры программы, содержащей функции main, fl и f2:

1.директивы препроцессора

2.описания

int main(){

операторы главной функции

}

int fl(){

операторы функции fl

}

int f2(){

операторы функции f2

}

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

Перегрузка функций.

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

4 float areaRectangle(float, float) //функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см)

{

return a * b; // умножаем длинны сторон прямоугольника и возвращаем полученное произведение

}

 

 

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

 

Предположим, что наши исходные данные (стороны прямоугольника) заданы в метрах и сантиметрах, например такие: a = 2м 35 см; b = 1м 86 см. В таком случае, удобно было бы использовать функцию с четырьмя параметрами. То есть, каждая длинна сторон прямоугольника передаётся в функцию по двум параметрам: метры и сантиметры.1

4 float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм)

{

return (a_m * 100 + a_sm) * (b_m * 100 + b_sm);

}

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

Теперь, самое главное – у нас есть две функции, с разной сигнатурой, но одинаковыми именами (перегруженные функции). Сигнатура – это комбинация имени функции с её параметрами. Как же вызывать эти функции? А вызов перегруженных функций ничем не отличается от вызова обычных функций, например:

areaRectangle( 32, 43); // будет вызвана функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см)

areaRectangle( 4, 43, 2, 12); // будет вызвана функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм)

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

#include <iostream>

using namespace std;

// прототипы перегруженных функций

float areaRectangle(float a, float b);

float areaRectangle(float a_m, float a_sm, float b_m, float b_sm);

int main()

{

cout << "S1 = " << areaRectangle(32,43) << endl; // вызов перегруженной функции 1

cout << "S2 = " << areaRectangle(4, 43, 2, 12) << endl; // вызов перегруженной функции 2

return 0;

}

// перегруженная функция 1

float areaRectangle(float a, float b) //функция, вычисляющая площадь прямоугольника с двумя параметрами a(см) и b(см)

{

return a * b; // умножаем длинны сторон прямоугольника и возвращаем полученное произведение

}

// перегруженная функция 2

float areaRectangle(float a_m, float a_sm, float b_m, float b_sm) // функция, вычисляющая площадь прямоугольника с 4-мя параметрами a(м) a(см); b(м) b(cм)

{

return (a_m * 100 + a_sm) * (b_m * 100 + b_sm);

}

Шаблоны функций.

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

 

На самом деле, шаблоны функций -это мощный инструмент в С++, который намного упрощает труд программиста. Например, нам нужно запрограммировать функцию, которая выводила бы на экран элементы массива. Задача не сложная! Но, чтобы написать такую функцию, мы должны знать тип данных массива, который будем выводить на экран. И тут нам говорят — тип данных не один, мы хотим, чтобы функция выводила массивы типа int, double, float и char.

 

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

// перегрузка функции printArray для вывода массива на экран

void printArray(const int * array, int count)

{

for (int ix = 0; ix < count; ix++)

cout << array[ix] << " ";

cout << endl;

}

 

void printArray(const double * array, int count)

{

for (int ix = 0; ix < count; ix++)

cout << array[ix] << " ";

cout << endl;

}

 

void printArray(const float * array, int count)

{

for (int ix = 0; ix < count; ix++)

cout << array[ix] << " ";

cout << endl;

}

 

void printArray(const char * array, int count)

{

for (int ix = 0; ix < count; ix++)

cout << array[ix] << " ";

cout << endl;

}

 

 

Таким образом, мы имеем 4 перегруженные функции, для разных типов данных. Как видите, они отличаются только заголовком функции, тело у них абсолютно одинаковое. Я написал один раз тело функции для типа int и три раза его скопировал для других типов данных.

 

И, если запустить программу с этими функциями, то она будет исправно работать. Компилятор сам будет определять какую функцию использовать при вызове.

 

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

 

Мы создаем один шаблон, в котором описываем все типы данных. Таким образом исходник не будет захламляться никому ненужными строками кода. Ниже рассмотрим пример программы с шаблоном функции. Итак, вспомним условие: «запрограммировать функцию, которая выводила бы на экран элементы массива».

#include <iostream>

#include <cstring>

using namespace std;

// шаблон функции printArray

template <typename T>

void printArray(const T * array, int count)

{

for (int ix = 0; ix < count; ix++)

cout << array[ix] << " ";

cout << endl;

} // конец шаблона функции printArray

int main()

{

// размеры массивов

const int iSize = 10,

dSize = 7,

fSize = 10,

cSize = 5;

// массивы разных типов данных

int iArray[iSize] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

double dArray[dSize] = {1.2345, 2.234, 3.57, 4.67876, 5.346, 6.1545, 7.7682};

float fArray[fSize] = {1.34, 2.37, 3.23, 4.8, 5.879, 6.345, 73.434, 8.82, 9.33, 10.4};

char cArray[cSize] = {"MARS"};

cout << "\t\t Шаблон функции вывода массива на экран\n\n";

// вызов локальной версии функции printArray для типа int через шаблон

cout << "\nМассив типа int:\n"; printArray(iArray, iSize);

// вызов локальной версии функции printArray для типа double через шаблон

cout << "\nМассив типа double:\n"; printArray(dArray, dSize);

// вызов локальной версии функции printArray для типа float через шаблон

cout << "\nМассив типа float:\n"; printArray(fArray, fSize);

// вызов локальной версии функции printArray для типа char через шаблон

cout << "\nМассив типа char:\n";printArray(cArray, cSize);

return 0;

}

Заметьте, код уменьшился в 4 раза, так как в программе объявлен всего один экземпляр функции — шаблон. В main я объявил несколько массивов — четыре, для типов данных: int, double, float, char. После чего, в строках 26, 28, 30, 32, выполняется вызов функции printArray для разных массивов. Обратите внимание, что перед объявлением самой функции, в строке 5, стоит следующая запись template<typenameT>. Как раз эта запись и говорит о том, что функция printArray на самом деле является шаблоном функции, так как в первом параметре printArray стоит тип данных const T*, точно такой же как и в строке 5.

 

Все шаблоны функций начинаются со слова template, после которого идут угловые скобки, в которых перечисляется список параметров. Каждому параметру должно предшествовать зарезервированное слово class или typename.1 template <class T>

 

 

или1 template <typename T>

 

 

или1 template <typename T1, typename T2>

 

 

Ключевое слово typename говорит о том, что в шаблоне будет использоваться встроенный тип данных, такой как: int, double, float, char и т. д. А ключевое слово class сообщает компилятору, что в шаблоне функции в качестве параметра будут использоваться пользовательские типы данных, то есть классы.

 

У нас в шаблоне функции использовались встроенные типы данных, поэтому в строке 5 мы написали template<typenameT>. Вместо T можно подставить любое другое имя, какое только придумаете. Давайте подробно рассмотри фрагмент кода из верхней программы, я его вынесу отдельно.1

8 // шаблон функции printArray

template <typename T>

void printArray(const T * array, int count)

{

for (int ix = 0; ix < count; ix++)

cout << array[ix] << " ";

cout << endl;

} // конец шаблона функции printArray

 

 

В строке 2 выполняется определение шаблона с одним параметром — T, причем этот параметр будет иметь один из встроенных типов данных, так как указано ключевое слово typename.

 

Ниже, в строках 3 — 8 объявлена функция, которая соответствует всем критериям объявления обычной функции, есть заголовок, есть тело функции, в заголовке есть имя и параметры функции, все как обычно. Но что эту функции превращает в шаблон функции, так это параметр с типом данных T, это единственная связь с шаблоном, объявленным ранее. Если бы мы написали1

6 void printArray(const int * array, int count)

{

for (int ix = 0; ix < count; ix++)

cout << array[ix] << " ";

cout << endl;

}

 

 

то это была бы простая функция для массива типа int.

 

Так вот, по сути T — это даже не тип данных, это зарезервированное место под любой встроенный тип данных. То есть когда выполняется вызов этой функции, компилятор анализирует параметр шаблонированной функции и создает экземпляр для соответственного типа данных: int, char и так далее.

 

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

Надеюсь основную мысль по шаблонам функций до вас довел. Для закрепления материала, давайте рассмотрим еще один пример программы, с использованием шаблона функции.1

24 #include <iostream>

#include <cstring>

using namespace std;

// шаблон функции для поиска максимального значения в массиве

template <typename T>

T searchMax(const T* array, int size)

{

T max = array[0]; // максимальное значение в массиве

for (int ix = 0; ix < size; ix++)

if (max < array[ix])

max = array[ix];

return max;

}

int main()

{

// тестируем шаблон функции searchMax для массива типа char

char array [] = "aodsiafgerkeio";

int len = strlen(array);

cout << "Максимальный элемент массива типа char: " << searchMax(array, len) << endl;

// тестируем шаблон функции searchMax для массива типа int

int iArray [5] = {3,5,7,2,9};

cout << "Максимальный элемент массива типа int: " << searchMax(iArray, 5) << endl;

return 0;

}

Вот вам еще один пример использования шаблонов функций. Шаблон функции объявлен в строках 5-13. Функция должна возвращать максимальное значение массива, поэтому возвращаемое значение типа T, ведь тип данных массива заранее не известен. Кстати внутри функции объявлена переменная max типа T, в ней будет храниться максимальное значение массива. Как видите, тип данных T используется не только для спецификации параметров функции, но и для указания типа возвращаемого значения, а также может свободно использоваться для объявления любых переменных внутри шаблона функции.

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

Пример модуля. Способ использования модуля.

26.Массивы в языке С++. Связь указателей и массивов в С++.

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

 

float а [10]; // описание массива из 10 вещественных чисел

 

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

 

int b

© 2013 wikipage.com.ua - Дякуємо за посилання на wikipage.com.ua | Контакти