Title: ИиП

Q: 1. Основные парадигмы программирования. Преимущества и недостатки.
A: Парадигма программирования — совокупность идей и понятий, определяющая стиль написания программ. Парадигма является базовой программной единицей, в качестве которой может выступать определение (декларативное, функциональное программирование), действие (императивное), объект(объектно-ориентированное) и другие сущности.
В принципе, парадигмы программирования могут давать различный эффект
для различных данных и ситуаций, то есть в каких-то случаях проще использовать логическое программирование, в каких-то функциональное, в каких-то объектноори
ентированное.
Если говорить в общем плане, то программирование делится на императивное и декларативное. Императивная парадигма программирования описывает процесс
вычисления в виде набора инструкций, меняющих состояние программы. Декларативная парадигма программирования описывает не процесс, а свойства какой-то базовой
единицы.
Декларативная парадигма программирования подразделяется, в основном, нафункциональную и логическую.
Функциональное программирование предполагает использование функций в ихматематическом смысле, то есть результат функции зависит только от входных параметров и результатов других функций и не предполагает изменения состояния памяти.
Среди наиболее известных языков для функционального программирования LISP, F#,
SML, Haskell и т.д.
Логическое программирование основано на математической логике. Парадигма хорошо подходит для решения задач, в которых действуют объекты и отношения
между объектами, в частности, для задач искусственного интеллекта. Наиболее известный язык - Prolog.
С усложнением проектов появилась идея модульного программирования. Это подразумевает что программа представляет собой набор независимых блоков (набор
подпрограмм), каждая из которых имеет свой контролируемый размер, четкое назна
чение и детально проработанный интерфейс с внешней средой.
Достоинства:
∙ Успешно создаются достаточно крупные проекты.
∙ Уменьшается время разработки.
∙ Облегчается возможность модификации за счет использования стандартных под
программ.
Недостатки:
∙ Глобальные данные и неограниченность доступа к ним сильно усложняет струк
Ютуру программы и приводит к ошибкам.
∙ Очень трудоемкий процесс внесения любых, даже самых небольших изменений.
И, наконец, модульное программирование не отражает картину реального мира.
В итоге, пришли к идеям объектно-ориентированного программирования,основные положения были разработаны Кэйем и Ингаллсом. ООП использует идеюобъединения данных и методов их обработки в специальные конструкции,получившие название объектов. В качестве объекта может выступать что и кто угодно (люди,
хранилища данных, типы данных и т. д.). Программа, разработанная на основе тех нологии ООП, представляет собой совокупность объектов, которые взаимодействуют посредством вызова подпрограмм друг друга. Упростить процесс написания программ
по технологии ООП, позволяют такие понятия, как класс, объект, инкапсуляция, на
следование и полиморфизм.
Преимущества:
1. Логичность. ООП соответствует образу человеческого мышления (объекты, их
характеристики и возможности), а не команды, переменные и операции.
2. Повторное использование кода в других приложениях.
3. Высокая скорость разработки за счет первых двух пунктов. В идеале, приложе
ние не пишется, а собирается из готовых кусков с написанием незначительных
по объему «связок».
Есть и недостатки:
1. Большие временные затраты на проектирование, тормозит начальную фазу, но окупается на последующих этапах разработки ПО.
2. Снижение скорости работы приложения. Поддержка ООП требует больших затрат как по времени работы, так и по объему занимаемой памяти, по сравнению
с другими технологиями.
По уровню языки
программирования делятся на
∙ Языки низкого уровня.
∙ Языки высокого уровня.
Языком самого низкого уровня является ассемблер, который представляет каж
дую команду машинного кода с помощью символьных условных обозначений, назы
ваемых мнемониками.
Языки высокого уровня разработаны для человека, они ему ближе и понятнее, поэтому, для перевода языка высокого уровня на машинный, созданы трансляторы.
Они подразделяются на интерпретаторы и компиляторы.
Интерпретатор берет очередной оператор языка из текста программы, ана лизирует его структуру и сразу исполняет.
Компилятор полностью обрабатывает весь текст программы, просматривает в
поисках синтаксических ошибок, проводит смысловой анализ и автоматически переводит весь текст на машинный язык, при этом нередко выполняется оптимизация с помощью набора методов, позволяющих повысить быстродействие программы.

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

Q: 3. Основные элементы языка С++. Лексемы, ключевые слова, идентификаторы,
константы.
A: Любой язык содержит следующие базовые элементы: алфавит, лексемы, выраже ния, операторы. Лексемы образуются из символов, выражения — из лексем и символов, операторы — из символов, выражений и лексем.
Итак,
∙ Алфавит языка — это основные неделимые знаки, с помощью которых пишутся все тексты на языке (например, в русском языке — буквы).
∙ Лексема — минимальная единица языка, имеющая самостоятельный смысл (в русском языке — слово).
∙ Выражение — правило вычисления некоторого значения (в русском языке — словосочетание).
∙ Оператор — законченное описание некоторого действия (в русском языке — предложение).
Константы — переменные, не изменяющие своего значения в течение работы про
граммы.
Константы бывают именованные и неименованные.
Именнованные константы определяются точно также, как переменные, только перед описанием типа ставят ключевое слово const:
const int x = 5; //константа типа int.
Именованные константы должны быть обязательно инициализированы. То есть, описание вида const int x; приведет к ошибке.
Неименованные константы разделяются на целочисленные, вещественные, символьные и строковые.
Неименованные целочисленные константы могут быть записаны в одной из трех
форм:
∙ десятичная, например, 15, 654;
∙ восьмеричная — слева к записи числа добавляется 0, например, 061, 0251;
∙ шестнадцатеричная — слева к записи числа добавляется 0х или 0X, например, 0х15,
0X6А.
Неименованные вещественные константы могут быть записаны в одном из двух
видов:
∙ число с фиксированной точкой. Например, 0.000015, -14000.08. Дробная часть отде
ляется от целой точкой.
∙ число с плавающей точкой (в экспоненциальной форме). Например, 1.5Е-5, -1.400008
Е+4, -8.7e+7.
Неименнованные символьные константы представляют собой один или два символа (\ + некий символ), заключенных в апострофы. Например, ’a’, ’-’, ’W’, ’\n’
, ’\t’..
Символ обратной косой черты используется для представления:
∙ кодов, не имеющих графического изображения (например, ’\а’ — звуковой сигнал, ’\n’ — перевод курсора в начало следующей строки);
∙ символов апострофа (’) , обратной косой черты (\), знака вопроса (?) и кавычки
(");
∙ любого символа с помощью его шестнадцатеричного или восьмеричного кода, на пример, \073, \0xF5. Числовое значение должно находиться в диапазоне от 0 до
255.
Последовательности символов, начинающиеся с обратной косой черты, называют управляющими, или «escape»-последовательностями. В таблице 1 приведены их допусти
мые значения.
Строковые константы заключаются в кавычки. Внутри строковых констант могут использоваться управляющие последовательности. Примеры:
"Hello, my friend! \n"
Лексемы:
идентификаторы,
ключевые слова,
знаки (символы) операций,
литералы,
разделители.
Идентификаторы:
Имени объекта или переменной
Имени класса, структуры или объединения
Имени перечисленного типа
Члена класса, структуры, объединения или перечисления
Функции или функции члена класса
Имени определения типа (typedef)
Label name
Имени макроса
Параметра макроса
Ключевые слова — это предварительно определенные зарезервированные идентификаторы, имеющие специальные значения. Их нельзя использовать в качестве идентификаторов в программе. Примеры: delete, continue,do, while, elese и т.д

Q: 4. Операнды, выражения, операции. Приоритет операций.
A: В любой программе необходимо выполнять вычисления. Для вычисления значений используются вычисления, которые состоят из операндов, знаков операций и скобок. Операнды задают данные для вычислений, операции задают действия. Скобки расставляют приоритеты. Каждый операнд является выражением или его частным случаем, например,
константой или переменной.
3.1. Основные операции
Рассмотрим операции, определенные в языке С++ в порядке уменьшения их при
оритета: Более приоритетными являются унарные операции (для которых требуется один
операнд).
Инкремент и декремент Увеличение и уменьшение значения переменной на единицу.
Эти операции имеют вид: ++ и --
Операция определения размера sizeof предназначена для определения размера объекта или типа в байтах. Имеет две формы:
∙ sizeof(выражение) — количество байт, занимаемых результатом выражения;
∙ sizeof (тип) — количество байт, занимаемых переменной соответствующего
типа.
Операции отрицания
∙ Поразрядное отрицание (~) — инвертирует каждый разряд в двоичном представлении целочисленного операнда.
Операции работы с памятью взятие адреса (&), разадресация (*), выделение памяти (new), освобождение памяти (delete)
Арифметические операции: умножение (*), деление (/), остаток от деления (%) (опре
делена только для целочисленных операндов), сложение (+), вычитание (-).
Операции сдвига: сдвиг влево (<<), сдвиг вправо (>>) применяются к целочисленным
операндам. Сдвигают двоичное представление первого операнда влево или вправо на
количество разрядов, заданное вторым операндом.
Операции сравнения: меньше (<), меньше или равно (<=), больше (>), больше или рав
но (>=), равно (==), не равно (!=) сравнивают первый операнд со вторым.
Поразрядные операции: применяются только к целочисленным операндам, работают с двоичными представлениями. Сравнение происходит побитово.
При поразрядной конъюнкции или поразрядном И (&) бит равен 1 тогда и только тогда, когда соответствующие биты операндов равны 1.
При поразрядной дизъюнкции или поразрядном ИЛИ (|) бит равен 1 тогда, когда хотя бы один из соответствующих битов равен 1.
При поразрядном исключающем ИЛИ (^) бит результата равен 1 тогда, когда соот
ветствующий бит только одного из операндов равен 1.
Логические операции: (&& и ||). Операнды логических операций И (&&) и ИЛИ (||)
могут быть любых типов.

Q: 5. Понятие переменной. l- и r-концепция.
A: Место, в котором хранятся данные в компьютере, называется объектом. Для доступа к объекту необходимо знать его имя. Именованный объект называется переменной, имеющий конкретный тип. Данные, записанные в переменные, называют значениями.
Перед использованием любая переменная должна быть определена. При описании переменной определяется ее имя и тип. Пример описания:
int x, y; //описаны две переменные x и y типа int;
Имя переменной может содержать латинские буквы, цифры и символ подчеркивания, причем начинаться может только с буквы (можно начинать с символа подчеркивания, но такая запись зарезервирована для встроенных констант, поэтому лучше ее не использовать). Прописные и строчные буквы различаются, поэтому x и X — это разные переменные.
Имя переменной не может совпадать с зарезервированными словами.
Все переменные являются l-values. l-value (в переводе «л-значение», произносится как «ел-валью») – это значение, которое имеет свой адрес в памяти. Поскольку все переменные имеют адреса, то они все являются l-values (например: переменные a, b, c – все являются l-values). l от слова «left», так как только значения l-values могут находиться в левой стороне в операциях присваивания (в противном случае мы получим ошибку). Например, выражение 9 = 10; вызовет ошибку компилятора, так как 9 не является l-value. Число 9 не имеет своего адреса в памяти и, таким образом, мы ничего не можем ему присвоить (9 равно 9, и ничего здесь не изменить).
Противоположностью l-value является r-value (в переводе «р-значение», произносится как «ер-валью»). r-value – это значение, которое не имеет постоянного адреса в памяти. Примерами могут быть единичные числа (например, 7, которое вычисляется в 7) или выражения (например, x + 3, которое вычисляется в значение "х плюс 3").

Q: 6. Понятие типа данных. Основные типы данных. Приведение типов.
A: Тип данных — одно из основных понятий любого языка программирования. Позво ляет компилятору определять характеристики переменных и констант, использующихся в программе.
То есть, тип данных определяет:
1. характеристику переменной или константы (целое число, символ и т. д.);
2. диапазон значений;
3. размер памяти, занимаемой этой переменной или константой (зависит от разрядно
сти системы), определяется с помощью операции sizeof();
4. операции, допустимые для данного типа.
В любом языке, в том числе C++, существуют встроенные типы и типы, опреде
ленные пользователем:
Встроенные типы:
Целочисленный int
Вещественный double float
Символьный char
Логический bool
Модифицированные К ним относятся массивы, строки, перечисления, структуры, клас
сы и т. д.
Приведение типов
Это означает, что каждое значение переменной, аргумента функции и возвращаемого функции сохраняет приемлемый тип данных, и что операции, включающие значения различных типов", "имеют смысл" и не вызывают потери данных, неправильной интерпретации битовых шаблонов или повреждения памяти. Программа, которая никогда явно или неявно преобразовывает значения из одного типа в другой является типобезопасной по определению. Однако иногда требуются преобразования типов, даже небезопасные преобразования. Например, может потребоваться сохранить результат операции с плавающей запятой в переменной типа intили передать значение в unsigned int функцию, которая принимает значение signed int.

Q: 7. Основные конструкции языков программирования. Составные операторы. Время
жизни и область видимости переменной. Глобальные и локальные переменные.
A: Составные операторы
Составная инструкция состоит из нуля или нескольких операторов, заключенных в фигурные скобки ({ }). Составной оператор может использоваться везде, где ожидается оператор. Составные инструкции часто называются "блоками".
Время жизни и область видимости переменой
Все переменные имеют определенное время жизни (lifetime) и область видимости (scope). Время жизни начинается с момента определения переменной и длится до ее уничтожения. Область видимости представляет часть программы, в пределах которой можно использовать объект. Как правило, область видимости ограничивается блоком кода, который заключается в фигурные скобки. В зависимости от области видимости создаваемые объекты могут быть глобальными, локальными или автоматическими.
Глобальные
Глобальные переменные определены в файле программы вне любой из функций или любого другого блока кода и могут использоваться любой функцией. Глобальные переменные существуют в течение всей жизни программы и уничтожаются лишь с завершением программы.
Если глобальные переменные не инициализированы, то они получают нулевые значения.
Локальные
Локальные объекты
Объекты, которые создаются внутри блока кода (он может представлять функцию или какую-либо конструкцию типа циклов), называются локальными. Такие объекты доступны в пределах только того блока кода, в котором они определены.

Q: 8. Основные конструкции языков программирования. Операторы ветвления. Примеры.
A: Существуют два операторы ветвления: условный оператор и оператор выбора.
Оператор ветвления позволяет выбрать из двух вариантов, оператор выбора — из несколь
ких.
Под условным оператором понимается конструкция, когда при истинном значении
некого логического выражения выполняется один блок операторов, при ложном — другой.
Примеры
if (⟨условное выражение⟩) ⟨операторы «истина»⟩ [else ⟨операторы «ложь»⟩ ].
if (x < y)
cout << x;
else
cout << y;
Иногда полезно иметь возможность выбирать не из двух возможностей и большого
числа вложенных циклов, а сразу выбирать из нескольких возможностей. Для этой цели
служит оператор выбора.
Оператор выбора можно использовать только для выражений целых и символь
ных типов. Для вещественных переменных использование оператора выбора невозможно.
Примеры
Оператор switch имеет следующий формат:
switch (⟨выражение⟩){
case ⟨константное_выражение_1⟩ :[ ⟨блок_операторов_1⟩ ;[break;]]
case ⟨константное_выражение_2⟩:[ ⟨блок_операторов_2⟩ ;[break;]]
...
case ⟨константное_выражение_N⟩ :[ ⟨блок_операторов_N⟩;[break;]]
[ default:⟨операторы⟩]
}

Q: 9. Основные конструкции языков программирования. Операторы цикла. Примеры.
A: Цикл — это повторяющиеся определенное количество раз (до выполнения некоторого условия) операторы.
В языке C++ существует три вида операторов цикла: с предусловием, с постусловием и оператор цикла с параметром.
Оператор цикла с предусловием работает по следующему алгоритму:
1. Некоторому параметру присваивается начальное значение.
2. Если значение условия истинно, то выполняется блок операторов. Иначе переход к
шагу 4.
3. В блоке операторов изменяется параметр. Переход к шагу 2.
4. Выход из цикла
Оператор цикла с предусловием имеет следующий вид:
while (⟨логическое выражение⟩ ) { ⟨блок_операторов⟩;}
Ключевое слово while переводится как «пока», т. е., выполнять блок операторов
пока логическое выражение истинно.
int S = 0, N = 10, i = 1;
while (i <= N){
S += i ;
i++;
}
Цикл do while называется циклом с постусловием и имеет следующий формат:
1. Некоторому параметру присваивается начальное значение.
2. Выполняется блок операторов. В блоке операторов изменяется параметр.
3. Проверяется условие. Если оно истинное, переход к шагу 2. Иначе переход к шагу
4. Выход из цикла.
Например, найти сумму чисел от 1 до ? :
int S = 0, N = 10, i = 1;
do {
S += i ;
i++;
}
while (i <= N);
Оператор цикла с параметром работает по тому алгоритму, что и оператор цикла с предусловием. Это наиболее общий способ организации цикла.
Цикл for имеет следующий формат:
for (⟨ инициализация; логическое выражение; блок_модификаций⟩ )
{ ⟨блок_операторов⟩;}
Инициализация — присвоение одному или нескольким параметрам начального значения, логическое выражение — проверка условия окончания работы оператора цикла, блок модификаций — модификация одного или нескольких параметров оператора цикла.
Блок операторов выполняется до тех пор, пока логическое выражение истинно.
Любой из трех разделов (иногда и все три) может отсутствовать, обязательными являются только две точки с запятой:
for (; i <= N; i++)
for (int i = 1; i <= N; )
for (i = 1; ; i++)
for (; i <= N; )
for (; ;)
Например, найти сумму чисел от 1 до ? :
int S = 0; for (int i = 1; i <= N; i++)
S += i ;
Цикл с параметром выполняется по следующему алгоритму:
1. Сначала в разделе инициализации присваиваются начальные значения данным.
2. Проверяется истинность логического выражения. Если выражение истинно, переход
к шагу 3. Иначе переход к шагу 5.
3. Выполняется блок операторов.
4. Выполняется блок модификаций. Переход к шагу 2.
5. Конец цикла.

Q: 10. Основные конструкции языков программирования. Операторы управления. Примеры.
A: В языке C++ предусмотрены четыре оператора безусловного перехода: goto,
return, break, continue. Операторы goto и return можно применять в любом
месте программы, break и continue связаны с циклами, break используется также в операторы выбора switch.
Оператор безусловного перехода return служит для возврата управления из функции в точку вызова функции. Стандарты языка требуют, чтобы любая функция, кроме имеющих тип void, имела оператор return.
Синтаксис имеет следующий вид:
return ⟨значение⟩
Тип значения должен совпадать с типом функции.
Например,
int main(){
return 0;
}
Оператор goto используется крайне редко, поскольку нарушает логическую последовательность программы. Для этого оператора используются метки, любые буквенные выражения или целые числа. Метка и двоеточие после нее ставится в то место програм
мы, куда должно быть передано управление; в место, где необходимо прервать выполнение программы и вернуться к метку, ставится оператор вида: goto⟨имя метки⟩.
x = 1;
lab:
cout << x++ << "";
if (x < 100)goto lab;
Оператор прерывания циклов break используется при прерывании цикла, вне зависимости от истинности логического условия. В случае вложенных циклов прерывается
только цикл, содержащий break.
Например, найти все простые числа от 2 до ? :
int N = 20;
for (int i = 2; i <= N; i++){
bool fl = true;
for (int j = 2; j <= sqrt((float)i); j++)
if (j % i == 0){
fl = false;
break;
}
if (fl)cout << i << ""
}
Оператор прерывания continue прерывает только текущую итерацию цикла. Например, вывести на экран все числа, не кратные 5:
int N = 20;
for (int i = 2; i <= N; i++)
if (i % 5 == 0)continue;
else cout << i << "";

Q: 11. Составные типы данных. Указатели. Примеры.
A: Указатели представляют собой объекты, значением которых служат адреса других объектов (переменных, констант, указателей) или функций. Как и ссылки, указатели применяются для косвенного доступа к объекту. Однако в отличие от ссылок указатели обладают большими возможностями.
Для определения указателя надо указать тип объекта, на который указывает указатель, и символ звездочки *:
тип_данных* название_указателя;
Сначала идет тип данных, на который указывает указатель, и символ звездочки *. Затем имя указателя.
Например, определим указатель на объект типа int:
int* p;
Такой указатель может хранить только адрес переменной типа int, но пока данный указатель не ссылается ни на какой объект и хранит случайное значение.
Например, в моем случае консоль вывела "0x8" - некоторый адрес в шестнадцатеричном формате (обычно для представления адресов в памяти применяется шестнадцатеричная форма). Но также можно инициализировать указатель некоторым значением:
int* p{};
Поскольку конкрентное значение не указано, указатель в качестве значения получает число 0. Это значение представляет специальный адрес, который не указывает не на что.
С помощью операция & можно получить адрес некоторого объекта, например, адрес переменной. Затем этот адрес можно присвоить указателю::
int number {25};
int *pnumber {&number}; // указатель

Q: 12. Составные типы данных. Одномерные массивы. Примеры.
A: Массив — это структура данных, которая содержит множество значений, от
носящихся к одному и тому же типу. Тип может быть любым, как базовым, так и
составным. Каждое значение сохраняется в отдельном элементе массива, и компьютер сохраняет все элементы в памяти последовательно, друг за другом.
Существует два способа представления массивов: статические, когда память
под элементы массива выделяется на стадии компиляции и необходимо заранее знать размер массива, и динамические, когда память выделяется программистом с помощью
команды new на стадии выполнения программы.
Статический одномерный массив — это последовательность элементов одного
типа, размер которого должен быть известен заранее. Каждое значение хранится в отдельном элементе, и в памяти компьютера элементы массива располагаются после
довательно, один за другим.
int a[5] = {1, 3, 8, 9, 7};
Создать динамический массив достаточно просто с помощью операции new.
Для этого необходимо создать указатель определенного типа, и, используя new,
указать тип элементов и количество таких элементов в квадратных скобках:
int *mas = new int [10];
В данном примере указатель mas возвращает адрес нулевого элемента массива,
а всего память выделяется под 10 элементов типа int.
Если массив был создан с помощью операции new, соответственно необходимо
очистить память с помощью операции delete:
delete [] mas;
Пример:
int n = 5, i = 1;
6 int *mas = new int [n];

Q: 13. Составные типы данных. Вставка и удаление в одномерных массивах. Примеры.
A: Удаление элемента в массиве
Чтобы удалить элемент из массива, мы сначала должны определить индекс удаляемого элемента. Далее мы создаем указатель на этот элемент и сдвигаем все остальные элементы на одну позицию влево. Наконец, уменьшаем размер массива на единицу. Теперь удаленный элемент не будет доступен.
// создание массива
int* p = new int[10];
int n = 0;
cout << "Введите х: "; int x; cin >> x;
for (int i = 0; i < 10; i++)
cin >> p[i];
//
// нахождение четных элементов и кратных x
for (int i = 0; i < 10; i++)
if (p[i] % 2 && p[i]%x)
p[n++] = p[i];
//
// вывод
for (int i = 0; i < n; i++)
cout << p[i] << ' ';
cout << '\n';
//
delete[] p;
Вставка
Аналогичная ситуация как с удалением сдвигаем
int arr[50], i, elem, pos, tot;
cout<<"Enter the Size for Array: ";
cin>>tot;
cout<<"Enter "<<tot<<" Array Elements: ";
for(i=0; i<tot; i++)
cin>>arr[i];
cout<<"\nEnter Element to Insert: ";
cin>>elem;
cout<<"At What Position ? ";
cin>>pos;
for(i=tot; i>=pos; i--)
arr[i] = arr[i-1];
arr[i] = elem;
tot++;
cout<<"\nThe New Array is:\n";
for(i=0; i<tot; i++)
cout<<arr[i]<<" ";
cout<<endl;
return 0;

Q: 14.Составные типы данных. Двумерные массивы. Примеры.
A: Массив — это структура данных, которая содержит множество значений, от
носящихся к одному и тому же типу. Тип может быть любым, как базовым, так и
составным. Каждое значение сохраняется в отдельном элементе массива, и компьютер сохраняет все элементы в памяти последовательно, друг за другом.
Существует два способа представления массивов: статические, когда память
под элементы массива выделяется на стадии компиляции и необходимо заранее знать размер массива, и динамические, когда память выделяется программистом с помощью
команды new на стадии выполнения программы.
Это массив, который
удобно представлять в виде таблицы, состоящей из строк и столбцов. Тогда элемент массива с индексами i и j находится на пересечении i-ой строки и j-ого столбца массива. Двумерные массива также бывают статические и динамические.
Описание статического двумерного массива в общем случае имеет вид:
⟨тип⟩ ⟨имя массива⟩[⟨число строк⟩][⟨число столбцов⟩].
Например, запись вида int mas[10][10]; означает, что создан массив,
состоящий из 10 строк и 10 столбцов, элементы которого целые числа.
Объявление двумерного динамического массива имеет свои особенности. Так
как речь идет об указателях, то сначала с помощью оператора new выделяется память под массив указателей:
⟨тип⟩ **⟨имя массива⟩ = new ⟨тип⟩ *[⟨число строк⟩].
Затем для каждого элемента одномерного массива указателей выделяется память под
соответствующий массив:
for(i = 0; i < ⟨число строк⟩; i++)
⟨имя массива⟩[i] = new ⟨тип⟩ [⟨число столбцов⟩]
int **a = new int *[n]; //выделение памяти под массив указателей
11 for (int i = 0; i < n; i++)
12 a[i] = new int [m];

Q: 15. Составные типы данных. Вставка и удаление в двумерных массивах. Примеры.
A: Вставка
Аналогично сдвиг
// основа
bool evenOnly;
int column = n;
for (int i = 0; i < n; i++)
{
evenOnly = true;
for (int j = 0; j < column; j++)
{
if (arr[j][i] % 2 != 0)
{
evenOnly = false;
break;
}
}
if (evenOnly)
{
for (int j = n; j > i; j--)
{
for (int k = 0; k < column; k++)
{
arr[k][j] = arr[k][j - 1];
}
}
n++;
i++;
}
}
//
Удаление
Аналогично
//Удаление строк с минимальным элементом
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
if (mat[i][j] == min) // проверка условия
{
for (int k = i; k < n - 1; k++)
for (int g = 0; g < m; g++) // удаление
{
mat[k][g] = mat[k + 1][g];
}
n--;
i--;
break;
}
}
}
//

Q: 16. Составные типы данных. Строки. Примеры.
A: В языке С++ существует два вида строк — строки, представляющие собой массив символов, завершающиеся нулевым байтом (иногда называемые C-строки), и строки, реализованные в виде класса.
Для того, чтобы объявить строку в стиле С, необходимо описать ее как массив
символов, но при описании переменной зарезервировать память под нулевой байт: char
⟨имя массива⟩ [ ⟨длина строки + 1⟩ ].
Следовательно, для переменной str, содержащей 10 символов, необходимо описать переменную вида char str[11] или char *str
Для работы с такими строками реализовано достаточное количество функций, ко торые описаны в библиотеке или :
size_t — встроенный беззнаковый тип, используемый для работы с данными, связанными, в основном, с номером позиции символа в строке.
size_t strlen(char *s1) определяет длину строки.
Листинг 1.1.
1 char *s1 = "Hello.";
2 size_t k = strlen(s1);
3 cout << "Size of string is " << k << endl;
Класс string
Для работы с этим типом строк необходимо подключить библиотеку .
Большая часть данных построена в соответствии с методами обобщенного программиро
вания и заголовки большинства функций соответствуют библиотеке STL.
Для описания используется тип string. Так как этот тип представляет собой
класс, то, при описании переменных он инициализируется конструктором по умолчанию,
а именно, пустой строкой.
Для ввода строки можно воспользоваться оператором <<. Но данный оператор
определяет пробел в качестве разделителя, но в случае строки пробел — это такой же
символ, как и все остальные. Поэтому, для работы со строками лучше воспользовать
ся функцией getline(). Данная функция считывает всю строку целиком. Имеет два
варианта:
istream& getline(istream& instream, string& str, char razd) — считывает из входного
потока instream набор символов до разделителя razd и записывает данные в
строку str.

Q: 17. Составные типы данных. Структуры. Примеры.
A: Для простой программы достаточно использовать базовые типы данных. Но чащевсего намного лучше использовать структурированные типы данных. Это типы данных, которые формируются пользователем на основании базовых типов.
Одним из примеров структурированных типов данных является массив, набор элементов одного типа. Если необходимо, чтобы тип содержал несколько разных данных
разных типов, используются структуры.
Массив — это набор элементов одинакового типа. Структура — это набор элементов
произвольных типов.
Синтаксис структуры:
struct ⟨имя типа⟩ {
⟨тип 1⟩ ⟨имя 1⟩
⟨тип 2⟩ ⟨имя 2⟩
⟨тип 3⟩ ⟨имя 3⟩
⟨тип N⟩ ⟨имя N⟩
};
Создается новый тип, состоящий из полей (в английской версии — members). Описываются поля — имя и тип переменной. Определение структуры заканчивается точкой с
запятой после закрывающей фигурной скобки.
Переменные созданного типа можно объявлять так же как и остальные типы. Единственная операция, доступная для переменной типа struct — операция присвоения одной переменной другой. Присвоение означает, что каждому полю одной переменной присваивается значение соответствующих полей другой переменной.
Индивидуальные поля достижимы либо с помощью операции «точка», либо, в случае использования указателей, с помощью «операции разыменования указателей на структуры» (->).
6 struct point{//структура точка
7 double x, y;
8 };
9
10 point *create(int &n){//создание массива точек
11 cout << "n = ";
12 cin >> n;
13 point *a = new point[n]; //массив точек
14 for(int i = 0; i < n; i++){
15 cout << "Input " << i << " point coordinates (x, y): \n";
16 cin >> a[i].x >> a[i].y;
17 }
18 return a;
19 }

Q: 18. Работа с потоками. Примеры.
A: Система ввода/вывода создает между источником и приемником данных единообразный интерфейс, не зависящий от физических устройств. Это абстрактное средство связи называется потоком.
Основными операциями ввода и вывода являются перегруженные операторы сдвига влево << («прочесть из») и вправо >> («записать в»). Автором языка С++ Б. Страуструпом были выбраны эти операции из-за того, что, во-первых, они достаточно редко встречаются, во-вторых, имеют достаточно низкий приоритет, что позволяет не использо
вать скобки при выводе. Круглые скобки необходимо при выводе выражений, содержащих логические выражении и, естественно, операции сдвига влево и вправо.
Все данные, необходимые для работы со потоками, находятся в библиотеке <
iostream>.
Как и в случае работы со строками, для потоков определен свой тип данных,
используемый для работы с числами (количество позиций, текущая позиция и т. д.):
streamsize
Потоки делятся на стандартные (для работы с потоками ввода с клавиатуры и потоками вывода на экран), файловые (для работы с файлами) и строковые (для работы
со строками).
По своему действию потоки делятся на входные, выходные и двунаправленные.
Стандартные
При подключении библиотеки генерируется два стандартных потока
ввода: cin и wcin (для ввода символов типа wchar_t).
При подключении библиотеки создаются стандартные потоки вывода:
cout (поток вывода на экран), cerr и clog (небуферированный и буферированный,
соответственно, потоки вывода ошибок) для вывода символов типа char и wcout wcerr и wclog для вывода символов типа wchar_t.
Для вывода данных используются операция вывода (<<), а также функции cout.
put(c) — для вывода символа и cout.write(str, n) — для вывода n символов строки
str.
Файловые
Под файлом в языке программирования понимается любое устройство откуда можно считывать данные либо куда можно записывать данные.
Работа с файлами может быть описана следующим алгоритмом:
1. Создание потока.
2. Связывание потока с файлом.
3. Считывание из файла / запись в файл.
4. Закрытие файла.
Как и в случае со стандартными потоками, файловые потоки бывают входными
(чтение из файла), выходными (запись в файл) и двунаправленными.
Для работы с файловым потоком необходимо подключить библиотеку .
Поскольку файловые потоки могут использоваться в разных функциях, чаще всего файловый поток создаются в качестве глобальной переменной.
В библиотеке определены классы ifstream (входной поток), ofstream (выходной поток) и fstream (двунаправленный поток).
Создание потока означает создать объект соответствующего класса:
ifstream in; //файловый поток ввода
ofstream out; //файловый поток вывода
fstream io; //файловый поток ввода-вывода
Имя потока может быть любое, желательно понятное. Можно создать несколько потоков ввода или вывода:
ifstream in0, in1, in2; //файловый поток ввода
Строковые
В языке C++ есть возможность считывать данные из строк (как будто бы из файла) и выводить данные в строки.
Для этого используются описанные в заголовочном файле sstream объекты istringstream (поток для чтения из строки) и ostringstream(поток для вывода из строки).
Строковые потоки — это универсальные средства перевода внутренних двоичных значений в строки (выходной строковый поток) и обратно (входной строковый поток). Строковые потоки объектно-ориентированной библиотеки могут быть и двунаправленными. Кроме того, при работе с ними нам нет необходимости резервировать память для строк. И наконец, со строковыми потоками можно использовать все средства форматирования или собственные написанные манипуляторы.
По направлению обмена потоки можно разделить на входные (данные вводятся в память), выходные (данные выводятся из памяти) и двунаправленные (допускающие как извлечение, так и включение).
ios — базовый класс потоков;
istream — класс входных потоков;
ostream — класс выходных потоков;
iostream — класс двунаправленных потоков;
istringstream — класс входных строковых потоков;
ostringrstream — класс выходных строковых потоков;
stringstream — класс двунаправленных строковых потоков;
ifstream — класс входных файловых потоков;
ofstream — класс выходных файловых потоков;
fstream — класс двунаправленных файловых потоков.

Q: 19. Файловые потоки. Файлы произвольного и последовательного доступа. Примеры.
A: Под файлом в языке программирования понимается любое устройство откуда можно считывать данные либо куда можно записывать данные.
Работа с файлами может быть описана следующим алгоритмом:
1. Создание потока.
2. Связывание потока с файлом.
3. Считывание из файла / запись в файл.
4. Закрытие файла.
Как и в случае со стандартными потоками, файловые потоки бывают входными (чтение из файла), выходными (запись в файл) и двунаправленными.
Для работы с файловым потоком необходимо подключить библиотеку .
Поскольку файловые потоки могут использоваться в разных функциях, чаще всего файловый поток создаются в качестве глобальной переменной.
В библиотеке определены классы ifstream (входной поток), ofstream
(выходной поток) и fstream (двунаправленный поток).
Создание потока означает создать объект соответствующего класса:
ifstream in; //файловый поток ввода
ofstream out; //файловый поток вывода
fstream io; //файловый поток ввода-вывода
Имя потока может быть любое, желательно понятное. Можно создать несколько потоков ввода или вывода:
ifstream in0, in1, in2; //файловый поток ввода
Связывание потока с файлом с помощью операции open().
Файл для чтения должен существовать до начала работы с файлом. Файл для записи создается автоматически. Если открывается для записи уже существующий файл,
то все имеющиеся данные уничтожаются.
Файл связывается с потоком следующим образом:
in.open("input.txt");
Можно также объединить создание потока и связывание его с файлом:
ifstream in("input.txt"); //создать поток и связать с файлом input
.txt
Считывание из файла / запись в файл происходит с помощью тех же операторов и функций, что и работа со стандартными потоками:
∙ для чтения — оператор ввода (>>), функции get(), getline(), read().
Только вместо имени стандартного потока cin используется имя созданного
пользователем потока:
in >> x;
in.get(x);
in.getline(x,10.’#’); //для строк типа char
getline(in,str); //для строк типа string
∙ для записи — оператор вывода (<<), функции put(), write(). Только вместо
имени стандартного потока cout используется имя созданного пользователем
потока:
out << x;
out.put(x);
Закрытие файла происходит с помощью функции close():
out.close()
ifstream in("file_io.txt"); //для чтения
14 ofstream out("buffer.txt"); //для записи
15 while(in.peek() != EOF){ //пока не дошли до конца файла
16 in >> x; //считываем из 1 файла
17 out << x << endl; //записываем в буферный файл
18 }
19 in.close(); //закрываем файлы
20 out.close();
Файлы с произвольным доступом
В отличии от текстового
файла, бинарный файл представлен двоичными кодами данных, что приводит к меньшим потерям, чем в случае текстовых файлов, так как двоичные коды определяются ком
пьютером однозначно, в отличии от текстовых файлов. Иногда такие файлы называют типизированными, так как они содержат данные только одного типа.
Также бинарные файлы называют файлами с произвольным доступом — для того, чтобы считать, допустим пятый элемент, надо просто сдвинуть указатель на
5*sizeof(x) позиций файла.
Считывание и запись в бинарные файлы происходит только программным путем.
Для открытия файла необходимо использовать в качестве второго параметра
ios_base::binary:
ifstream in("myfile.dat", ios_base::binary)
Для считывания из файла используется только функция read:
in.read((char*)&x, sizeof(x));
Для записи в файл используется только функция write:
in.write((char*)&x, sizeof(x));
11 setlocale (LC_ALL, "russian");
12 int a1 = 1, a2 = 1, a, n = 10;
13 ofstream out("data.dat", ios_base::binary); //для записи
14 out.write((char*)&a1, sizeof(a1)); //запись первых чисел
15 out.write((char*)&a2, sizeof(a2)); //Фибоначчи
16 for (int i = 2; i < n; i++){//запись остальных
17 a = a1 + a2;
18 a1 = a2;
19 a2 = a;
20 out.write((char*)&a, sizeof(a));
21 }
22 out.close(); //закрываем файл
23 ifstream in("data.dat", ios_base::binary);//для чтения
24 in.seekg(2*sizeof(a)); //сдвиг на третью позицию
25 while(in.peek() != EOF){
26 in.read((char*)&a, sizeof(a)); //считываем число
27 cout << a << endl;
28 in.seekg(2*sizeof(a), ios_base::cur); //сдвиг на третью позицию отн. текущей
29 }
30 return 0;
Файлы с последовательным доступом
Последовательные файлы — файлы, хранящие информацию в неструктурированном (для поиска и обращения) виде. Поиск в таких файлах осуществляется последовательным считыванием файла с начала и сравнением «всего» с искомым. Так же и обращение к определённому участку файла каждый раз требует «чтения с начала».
Примером последовательных файлов являются текстовые файлы (*.txt)
setlocale (LC_ALL, "russian");
12 string x;
13 ifstream in("file_io.txt"); //для чтения
14 ofstream out("buffer.txt"); //для записи
15 while(in.peek() != EOF){ //пока не дошли до конца файла
16 in >> x; //считываем из 1 файла
17 out << x << endl; //записываем в буферный файл
18 }
19 in.close(); //закрываем файлы
20 out.close();
21 ifstream in1("file_oi.txt"); //для чтения
22 ofstream out1("file_io.txt"); //для записи
23 while(in1.peek() != EOF){ //пока не дошли до конца файла
24 in1 >> x; //считываем из 1 файла
25 out1 << x << endl; //записываем в буферный файл
26 }

Q: 20. Функции. Описание, объявление, вызов. Функции, не возвращающие значения.
Примеры.
A: Функция — подпрограмма, выполняющая некоторый набор операторов, в итоге, результатом является некоторое законченное действие.
В языке C++ вся программа представляет собой набор функций. Основной функцией, с которой взаимодействует операционная система, является функция int main()
{...}. Все остальные функции вызываются внутри int main(){...}.
При работе с функциями возможно три действия:
1. Объявление функции.
2. Определение функции.
3. Вызов функции.
Объявление функции (еще называют прототип функции) представляет собой заголовок функции, заканчивающийся точкой с запятой.
Заголовок функции содержит: тип возвращаемого значения, имя функции, а также число и типы параметров, которые требуется передать функции при ее вызове.
Синтаксис прототипа функции имеет вид:
⟨тип возвращаемого значения⟩ ⟨имя функции⟩ ( ⟨[число и типы параметров]⟩ );
Функция может не содержать ни одного параметра, но круглые скобки в заголовке функции обязательны:
float print();
Список параметров содержит все данные, необходимые для корректной работы функции. Параметры перечисляются через запятую, перед каждым пишется тип данного
параметра.
Например, прототип функции, определяющей площадь треугольника по трем сто
ронам:
float square (float, float, float);
Прототип функции может содержать имена параметров. Компилятор их игнорирует, но наличие имен позволяет легче понимать код программы.
float square (float a, float b, float c);
Любая функция должна быть определена и только один раз. Определение функции — это объявление функции (без точки с запятой в конце) плюс тело функции. Типы
и число параметров в объявлении и определении должны совпадать, имена параметров совпадать не обязаны.
Например, определение функции, определяющей площадь треугольника по трем
сторонам:
1 float square (float a, float b, float c){
2 float p = (a + b + c)/2; //полупериметр треугольника
3 float s = sqrt(p*(p - a)*(p - b)*(p - c)); //площадь по формуле
Герона
4 return s;
5 }
Для работы с функцией, ее необходимо вызывать. Вызовов функции может быть сколько угодно, в том месте, где это необходимо для правильной работы программы.
Вызов функции может быть либо в функции int main(), либо в любой другой
функции. Вызываемая функция должна быть до этого либо определена, либо объявлена. Функция, возвращающая значение, вызывается «справа», как самостоятельное выражение, либо в составе выражения:
1 int func (int a, float b){
2 //тело функции
3 return S;
4 }
5
6 int main(){
7 int a, b, f;
8 float c;
9 //операторы
10 cout << a + func (func (a, c), c);
11 //операторы
12 return 0;
13 }
Функция, не возвращающая значение, вызывается только «слева»:
1 void func (int a, float b){
2 //тело функции
3 }
4
5 int main(){
6 int a, b, f;
7 float c;
8 //операторы
9 func (a, c);
10 //операторы
11 return 0;
Типы и число параметров, использующихся в вызовах функции должны обязатель но совпадать с типами и числом параметров в объявлении или определении функции. Если типы не совпадают, то в случае отсутствия перегрузки, будет произведено неявное преобразование типов, при этом возможна потеря данных.

Q: 21. Функции. Параметры и аргументы. Передача параметра по значению, указателю
и ссылке.
A: Параметры (переменные)
Переменные, определенные внутри функции, называются локальными. Локальные переменные инициализируются каждый раз, как только поток исполнения достигает точ
ки их определения. Это происходит при каждом вызове функции. Каждый такой вызов приводит к созданию локальной переменной, которая существует только во время работы текущей функции.
Переменные, описанные вне всех функций, называются глобальными. Эти пере менные доступны во всех функциях. Но если, в функции определена переменная с тем
же именем, что и глобальная, то функция будет работать с локальной переменной. В этом случае возможны непредвиденные ситуации, поэтому желательно избегать глобальных
переменных.
Если глобальные переменные все-таки используются, то в случае конфликта для использования именно глобальной переменной необходимо использовать операцию досту
па: ::.
Листинг 1.5.
1 #include
2 using namespace std;
3
4 int a = 5; //глобальная переменная
5
6 void func(int x, int y){
7 x++; //изменение локальных переменных
8 y−−;
9 cout << "vnutri\n";
10 cout << "x = " << x << " y = " << y << " a = " << a << endl;
11 }
Статические переменные —
переменные, определенные с помощью спецификатора static. Такие переменные инициализируются лишь однажды при первом достижении точки определения переменной.
Переменные, используемые при объявлении или определении функции, называются формальными или параметрами.
Переменные, используемые при вызове функции, называются фактическими или аргументами.
Определение функции:
double square (double a, double b, double c);
Аргументы
Очень часто функции имеют больше аргументов, чем это необходимо. В таких случаях используют аргументы по умолчанию (особенно часто это встречается в конструкторе классов.) Для аргументов по умолчанию параметрам в списке может присваиваться некоторое значение:
Например, функция, печатающая числа в любой системе счисления. По умолчанию, числа печатаются в десятичной системе счисления.
void print(int value, int base = 10);
int main(){
print(31); //вызов по умолчанию
print(31, 8);
print(31, 2);
Если функция вызывается с одним аргументом, то в качестве второго параметра используется значение по умолчанию. Если функция вызывается с двумя аргументами, в качестве второго параметра используется введенное значение. В примере, первый вызов
напечатает число 31 в десятичной системе счисления, второй вызов — число 31 в восьмеричной системе счисления.
Аргументами по умолчанию могут быть аргументы, находящиеся только в конце
списка параметров:
int f(int a, int b = 0, int c = 1); //правильно
int f(int a = 0, int b, int c = 1); //неправильно
Существует два способа передачи данных в функцию: по значению и по адресу.
Каждый из этих способов применяется часто, в зависимости от ситуации.
Передача данных по значению используется чаще всего.
При передаче аргументов по значению функция получает копию значения переменных и констант. Например:
#include
void square(int); // прототип функции
int main()
{
int n {4};
std::cout << "Before square: n = " << n << std::endl;
square(n);
std::cout << "After square: n = " << n << std::endl;
}
void square(int m)
{
m = m * m; // изменяем значение параметра
std::cout << "In square: m = " << m << std::endl;
При передаче параметров по ссылке(адресу)передается ссылка на объект, через которую мы можем манипулировать самим объектов, а не просто его значением. Так, перепишем предыдущий пример, используя передачу по ссылке:
#include
void square(int&); // прототип функции
int main()
{
int n {4};
std::cout << "Before square: n = " << n << std::endl;
square(n);
std::cout << "After square: n = " << n << std::endl;
}
void square(int& m)
{
m = m * m; // изменяем значение параметра
std::cout << "In square: m = " << m << std::endl;
}
Параметры функции в C++ могут представлять указатели. Указатели передаются в функцию по значению, то есть функция получает копию указателя. В то же время копия указателя будет в качестве значения иметь тот же адрес, что оригинальный указатель. Поэтому используя в качестве параметров указатели, мы можем получить доступ к значению аргумента и изменить его.
Например, пусть у нас будет простейшая функция, которая увеличивает число на единицу:
#include
void increment(int);
int main()
{
int n {10};
increment(n);
std::cout << "main function: " << n << std::endl;
}
void increment(int x)
{
x++;
std::cout << "increment function: " << x << std::endl;
}

Q: 22. Рекурсия. Примеры.
A: Рекурсивная функция — вызов функцией самой себя.
Примеров рекурсивной функции достаточно много. Рекурсия встречается во всех областях науки.
Данные в рекурсивные функции передаются по значению, поэтому при каждом новом вызове происходит выделение дополнительной памяти под новые переменные, что при достаточной глубине рекурсии может привести к переполнению памяти. Именно это яв
ляется основным недостатком рекурсивных функций, поэтому, лучше пользоваться обычными функциями, а не рекурсивными.
Обычно работа рекурсивной функции представляет собой цепочку вызовов этой рекурсивной функции. Этот процесс происходит до тех пор, пока что-то не прервет эту цепочку. Чаще всего для этого используется оператор if, например, если условие истин
ное, то цепочка вызовов рекурсии прерывается.
Пример 1.2. Примером работы рекурсивной функции служит функция вычисления факториала:
Листинг 1.10.
1 #include
2 using namespace std;
3
4 int f(int n){
5 if (n < 0) return −1; //выход из рекурсии для отриц. чисел
6 if (n == 0 || n == 1) { //выход из рекурсии
7 cout << "n = " << n << " adress " << &n << endl;
8 return 1;
9 }
10 else {
11 cout << "n = " << n << " adress " << &n << endl;
12 return n*f(n − 1); //рекурсивный вызов
13 }
14 }
15 int main(){
16 cout << f(5) << endl;
17 return 0;
18 }

Q: 23. Перегрузка функций. Шаблоны функций.
A: В языке С++ существует возможность перегрузки функции, когда в одной программе можно использовать несколько функций с одинаковыми именами, но выполняющими
разные значения.
Компилятор определяет какую из функций использовать при вызове по сигнатуре вызываемой функции (список аргументов функции).
При использовании перегруженных функций необходимо следить за соответствием количества и типом формальных и фактических параметров, поскольку в данном случае
неявного преобразования типов не происходит.
Принято считать, что передача данных по ссылке и по значению — это одно и тоже, так как запись вызова функции при передаче по значению и по ссылке одинакова.
Пример работы перегруженной функции.
Пример 1.4. Создание и вывод двумерного массива различных типов данных:
Листинг 1.12.
1 #include
2 using namespace std;
3
4 int **create(int c, int n, int m){//создание int
5 int **a = new int *[n]; //выделение памяти
6 for (int i = 0; i < n; i++)
7 a[i] = new int [m];
8 for (int i = 0; i < n; i++) //ввод массива
9 for (int j = 0; j < m; j++)
10 cin >> a[i][j ];
11 return a;
12 }
13
14 void print(int **a, int n, int m){ //печать int
15 for (int i = 0; i < n; i++,cout << endl)
16 for (int j = 0; j < n; j++)
17 cout << a[i][j] << " ";
18 }
double **create(double c, int n, int m){//создание int
21 double **a = new double *[n]; //выделение памяти
22 for (int i = 0; i < n; i++)
23 a[i] = new double [m];
24 for (int i = 0; i < n; i++) //ввод массива
25 for (int j = 0; j < m; j++)
26 cin >> a[i][j ];
27 return a;
28 }
29
30 void print(double **a, int n, int m){ //печать int
31 for (int i = 0; i < n; i++,cout << endl)
32 for (int j = 0; j < n; j++)
33 cout << a[i][j] << " ";
34 }
35
36 char **create(char c, int n, int m){//создание int
37 char **a = new char *[n]; //выделение памяти
38 for (int i = 0; i < n; i++)
39 a[i] = new char [m];
40 for (int i = 0; i < n; i++) //ввод массива
41 for (int j = 0; j < m; j++)
42 cin >> a[i][j ];
43 return a;
44 }
45
46 void print(char **a, int n, int m){ //печать int
47 for (int i = 0; i < n; i++,cout << endl)
48 for (int j = 0; j < n; j++)
49 cout << a[i][j] << " ";
50 }
Для упрощения программы можно воспользоваться шаблоном функ
ции. То есть написать одну функцию для переменных некоторого типа X, а уже при вызове функции по списку параметров определять переменные какого типа подставлять вместо
типа X.
Для написания шаблона функции необходимо перед каждой функцией написать ключевую фразу:
template <typename имя типа>.
Здесь треугольные скобки являются обязательным элементом.
В списке формальных параметров функции обязательно должен быть параметр типа X.
Пример работы перегруженной функции.
Пример 1.5. Создание и вывод двумерного массива различных типов данных:
Листинг 1.13.
1 #include
2 using namespace std;
3
4 template <typename X>
5 X **create(X c, int n, int m){//создание
6 X **a = new X *[n]; //выделение памяти
7 for (int i = 0; i < n; i++)
8 a[i] = new X [m];
9 for (int i = 0; i < n; i++) //ввод массива
10 for (int j = 0; j < m; j++)
11 cin >> a[i][j ];
12 return a;
13
15 template <typename X>
16 void print(X **a, int n, int m){ //печать
17 for (int i = 0; i < n; i++,cout << endl)
18 for (int j = 0; j < n; j++)
19 cout << a[i][j] << " ";
20 }
21
22 int main(){
23 int n,m;
24 cout << "Input dimension\n";
25 cin >> n >> m;
26 int c = 1; //определение для int
27 cout << "Input int matrix:\n";
28 int **a = create(c,n,m);
29 cout << "Print int matrix:\n";
30 print(a,n,m);
31 cout << "Input double matrix:\n";
32 double d = 1;//определение для double
33 double **a1 = create(d,n,m);
34 cout << "Print double matrix:\n";
35 print(a1,n,m);
36 cout << "Input char matrix:\n";
37 char d1 = ’1’;//определение для char
38 char **a2 = create(d1,n,m);
39 cout << "Print char matrix:\n";
40 print(a2,n,m);
41 return 0;
42 }