Author: Русакова З.Н. Рудаков И.В.
Tags: вычислительная математика численный анализ радиоэлектроника алгоритмы учебное пособие вычислительная техника компьютерные технологии язык программирования c++
ISBN: 978-5-7038-6081-6
Year: 2023
Федеральное государственное бюджетное
образовательное учреждение высшего образования
«Московский государственный технический университет имени Н.Э. Баумана
(национальный исследовательский университет»
З.Н. Русакова, И.В. Рудаков
Структуры данных в С++
Учебное пособие
2-е издание, исправленное
В
А
Ч
Н
А
М
Р
Н
Р
Я
У
рМоскваИЗДАТЕЛЬСТВО
МГТУ ным. Н. 2. Баунаня
2023
УДК 519.682
ББК 32.073-018
PSS
Издание лоступно в электронном виде по адресу
https:/omstu.presscalalog/item/T977
Факультет «Информатика и системы управленияе
Кафедра «Программное обссиечение ЭВМ и информационные технологии»
Рекомендовано Научно-методическим советом
МГТУ им. Н.Э. Баумана в качестве учебного пособия
Рецензент
канд. техн. наук Г.Н. Ничушкина
Русакова, З.Н.
PHS
Структуры данных в C++ : учебное пособие / 3. Н. Русакова,
И. В. Рудаков. — 2-с изд, испр. — Москва: Издательство МГТУ
им. Н. Э. Баумана, 2023. — 157, [| с.
ISBN 978-5-7038-6081-6
Рассмотрены методики, илиомы и приемы решения залач обработки дннамичес
ких структур данных на языке С++. Полробио описаны вычислительные алгоритмы,
реализованные с использованием нотации указателей.
Привелены краткие теоретические сведения и примеры приложений по изучаемо
му материалу. Изложена метолика выполнения лабораторных работ по рассматривае
мым темам, которая используется авторами в процессе провеления практических за
нятий в МГТУ им. Н.Э. Баумана.
Для студентов, обучающихся по направлениям подготовки «Программная инже-
нерия» и «Ииформатика и вычислительная техника».
УДК 519.682
ББК 32.073-018
onenomcax м петочиноситлх Adamo ALDOR ACER TE by Sek MNO MORE.
i
Jeascodsme чдинолтели! Toxckiinid, apedidacena, od mike сообщения о залиеченитьих
Ppj infoi@bmsin gress
© МГТУ им. Н.Э. Баумана, 2020
© МГТУ им. Н.Э. Баумана, 2023,
с изменениями
© Оформление. Издательство
ISBN 978-5-7038-6081-6
МГТУ им. Н.Э. Баумана, 2023
ПРЕДИСЛОВИЕ
Учебное пособие препназначено лля поплержки выполнения лабораторного
практикума по лисциплине «Информатика».
Цель учебного пособия — содействовать формированию у студентов следую-
щих компетенций:
* влапение основными понятиями и методами обработки послепователь-
ностей простых переменных:
= владение основными понятиями препставления и обработки структур
данных произвопных типов. прелназначенных пля описания множества оп-
нотипных величин. семантически связанных между собой, с применением
нотации указателей:
* знание принципов алгоритмической декомпозиции и умение исполь-
зовать инструментальные средства ее представления языке С++ (пол-
программы-функции):
* влаление представлением абстрактных типов данных и их реализацией.
После практического освоения лиспиплины студент должен знать:
* способы приведения различных структур данных и алгоритмов их
обработки:
* приемы программирования статических и линамических структур
ланных:
* метолы перелачи параметров в полпрограммах-функциях (по значе-
нию. адресу. ссылке):
* инструментальные средства реализации абстрактных
типов данных и
их обработки.
Стулент должен уметь:
* выбирать полхоляптие структуры данных пля решения конкретной за-
лачи и инструментальные срелства их обработки-
* использовать инструментальные средства разработки подпрограмм,
реатизуемые в C++ Ear фУуНЕПИИ И ЕЛАССЫ.
Студент лотжен ымень Hoek
* самостоятельной работы с учебной и справочной литературой:
* освоения и использования приемов программирования пля решения
базовых вычислительных залач.
На конкретных
примерах в учебном пособии раскрыты метолы и приемы
решения заляч обработки сложных пользовательских (статических и линамиче-
ских) CIDVETVD ланных Потробню рассмотрено решение вычислительных залач,
4
Предисловие
реализованных на языке С++, с использованием нотации указателей. что по-
зволяет обрабатывать большие массивы данных и способствует быстродействию.
Учебное пособие состоит из шести глав.
Вглаее 1 рассматриваются базовые типы пля описания простых переменных,
простейптие конструкции языка, управляюттие операторы, программная реализация
структурных базовых алгоритмов обработки числовых последовательностей
простых переменных.
В главе 2 привелены статические одномерные и многомерные массивы и ба-
зовые алгоритмы их обработки! линамические переменные и массивы. алресуемые
указателями,
а также операции апресной арифметики.
Рассмотрены апгоритмы и
программы обработки динамических массивов, память для которых вылеляется
по требованию.
В главе 3 изложены принципы разработки подпрограмм на языке C++:
содержание интерфейса подпрограммы и механизмы перелачи параметров по
значению, по указателю, по ссылке.
Глава 4 посвяшена
вопросам прелставления и основным приемам обработки
строкс завершающим нулем.
В главе 5 приведены определение пользовательского типа — структуры и
основные приемы обработки
данных этого типа.
В главе 6 рассмотрено проектирование
приложений, разработанных
с приме-
нением принципов объектно-ориентированного
програмсмирования (ООП). Ompe-
пелены классы, использующие механизмы включения. композиции
и наслелова-
ния. Изложена методика обработки текстовых и бинарных файлов посредством
классов файловых потоков С++. Описаны линейные абстрактные
типы ланных
и их представление с помошью классов. Изложение сопровождается примерами
приемов программирования на языке С++.
Учебное пособие булет полезно при изучении практических основ современ-
ного программирования с использованием как пропелурного. так и объектно-
ориентированного полхола.
ВВЕДЕНИЕ
Язые программирования С++ — опин из наиболее мошных и широко
распространенных языков программирования. для которого существует об-
шедоступный межлунаролный стандарт языка С++, не зашишенный правом
собственности. Он объединяет в себе пропелурные возможности языка С,
принципы ООП. представленного классами, и обобщшенное программиро-
вание. полплерживаемое пгаблонами.
При описании метолов создания и использования многомерных массивов
и приемов их обработки используется подход. основанный на указателях и
динамических переменных. Массивы. структуры и указатели — три составных
типа C++. Стратегия указателей позволяет выделять неименованные области
памяти необхолимого объема во время выполнения программы.
доступ к кото-
рой осушествляется через указатели. Для их создания сушествует специальная
операция С++. возврашаюдщтая указатель на выделенную по запросу область.
Применение указателей способствует эффективной реализации перелачи
и возвращения структурных переменных при вызове функпии. Альтернатив-
ный способ перелачи аргумента — использование ссылочных переменных.
Конструкции языка в учебном пособии определены с помошью нотации,
прелложенной Дж Бэкусом и П. Науром. Форма Бэкуса — Наура (БНФ)—
форма метаязыка. используемая для описания синтаксиса языков програм-
мирования и широко применяемая в справочных системах компиляторов.
Нотация БНФ использует зарезервированные метасимволы, определяе-
мые понятия. символы алфавита языка или терминальные символы:
< > — имловые скобки обозначают определяемые понятия:
== — ЧИТАТЬ «определяется ЕаЕ».
| — читать «ити»:
[ ] — заключенная в квадратные скобки синтаксическая конструкция
может отсутствовать:
{ } — Фиурные скобки обозначают повторение.
Елжчое правило языкя в метаязыке имеет следукуший вил.
<Опрелеляемое понятие> "= серия альтернатив разделенных «|».
Альтернатива может содержать как определяемые понятия, так и терми-
нальныеЕ CHMEOTH
Глава 1. ОСНОВНЫЕ ПОНЯТИЯ C++. АЛГОРИТМЫ ОБРАБОТКИ
ПРОСТЫХ ПОСЛЕДОВАТЕЛЬНОСТЕЙ
В данной главе рассматриваются базовые типы для описания простых пере-
менных, простейшие конструкции языка, упраеляюттие операторы. принципы
структурного программирования и протраусмная реализация структурных алго-
ритмов обработки послеловательностей
простых переменных.
1.1. Типы данных и переменные. Структура программы
на языке С++. Линейные алгоритмы
Алфавит языка C++ состоит из букв латинского апфавита (прописных и
строчных), специальных символов и цифр. Лексемы языка — минимальные
семантически значимые конструкции языка: ключевые слова, имена объектов.
Внутри лексем разделители не допускаются. Имена переменных (илентифи-
каторы) полжны начинаться с буквы или символа полчеркивания. Регистры
букв различаются. Ключевые слова не могут быть идентификаторами.
Любые ланные в апгоритмических языках характеризуются своими ти-
пами. Тины — спенпиальные конструкции языка, которые рассматривают-
ся компилятором как образцы лля созлания других элементов программы
(переменных, констант и функций). Тип определяет формат внутреннего
представления объекта данного типа, т е. число смежных байтов памяти Из
этого вытекает ограничение на множество лопустимых значений и множество
допустимых операций.
Типы данных попразлеляют на базовые (фунламентальные) и пользо-
вательские (составные, производные) типы, определяемые пользователем.
Базовые типы опрелелены в языке и прелназначены
для описания простых
скалярных переменных. В языке С++ доопределены слелуницие базовые
типы ланных лля описания числовых символьных и логических переменных.
которым соответствуют ключевые слова:
char, int, float, double, void.
Тип char прелназначен пля описания символьных переменных. int —
пля представления целочисленных переменных. типы float и double — для
представления чисел с плавакицей точкой олинарной и лвойной точности.
Тип void служит пля описания переменной. не имеющей значений. г е. лля
1.1. Типы данных и переменные. Структура программы на языке C++.
7
опрелеления обобщенного указателя или функции, He имеющей значения.
К этим типам в C++ побавляются логический тип bool и расширенный
СИМВОЛЬНЫЙ ТИП снах t.
На основе стандартных
типов, опрелеленных
в языке, созлают описания
произволных типов, прелназначенных для описания множества величин, се-
мантически связанных межлу собой (массивов, структур, функций, классов,
указателей ссылки).
Все объекты программы должны быть объявленыпо их использования
и поименованы Описание состоитиз спецпификатора
типа и слелующего за
ним списка переменных, именицих этот тип. Кажлое описание заканчивается
точкой с запятой. Описание переменных:
<тип> «список ицентификаторов переменных»;
где <тип> — допустимый (простой или произволный) тип данных: <список
переменных» — идентификаторы переменных одного типа.
Переменные можно инициализировать. Леременная — зарезервирован-
ная физическая область памяти из некоторого числа байтов. Число байтов
пля внутреннего представления, лиапазон их значений и множество попу-
стимых операций определяются типом. У этой области есть свой adpec —
указатель на эту область памяти. Число байтов памяти, отвеленных для хра-
нения переменной, определяется ее типом. В эту область можно записать
значение, прочитать и изменить значение. С этой областью памяти связано
имя (идентификатор).
Например: int i, 7, п, m oat x, z, 9:
Основные характеристики переменных: тип, имя, значение, адрес, об-
ласть памяти. Тип определяет число байтов памяти, допустистимые операции,
внутреннее представление.
Число байтов памяти, выделяемых компилятором под переменные,
определяется фунецией sizeof (<тип>), возврашающей значение, равное
число байтов памяти.
Программа на языке C++ представляет собой набор определений отдель-
ных функций, описаний, директив препроцессора. Каждая программа обяза-
тельно должна включать в себя елинственную функцию с именем main() —
главную функцию, которая выполняется первой.
Связь между функциями осушествляется через параметры и возврашае-
мые функциями значения. Каждая функция имеет следующий вил:
<определение функции»::=<заголовок> <тело функции»
<заголовск>::=<тип возвращаемого значения <MMA функции>
[(=список параметров>)])
<тело функции-блок»;:={[<«описания переменных»] [<операторы»] }
Директивы препроцессора начинаются со знака #, их основное назна-
чение — подключение заголовочных файлов: # include химя файла».
Шаблон файла программы:
<директивы»
[<onMcaHMaA> |
$ Jaca |. Освояные понятия C++. Аллорытмы ofpadomcu mpocma nocieoeame nocmel
«главная функция>
[<описание подпрограмы-функций>»]
Функции можно попразлелить на лва класса: возврашанииие значение и
не возврашающие значение (void). Тип функции void соответствует про-
педурам
в Pascal или Delphi. Если тип фунЕции
Be Void. то в теле фунЕЦИИ
необхолим оператор return, возвращающий значение Фунеция может не
иметь списка передаваемых параметров и не возврашать значение.
Структура простейшей программы без полпрограмм. Тзезя программа
прел-
ставляет собой главную функцию. в которой есть объявление переменных и
программный код.
Шаблон опрелеления главной функции без параметров и не возврапта-
ющшей значение:
[объявления переменных] [операторы] }
Как следует из описания. главная функция может быть пустой:
Foid main{) {}
Это заглушка, используемая при тестировании созланного проекта в среле.
Программа с объявлениями переменных:
void main() {
float х1, м2, а, b;
int i,j,0;
Область действия переменных — часть протраммы. в которой переменные
могут быть использованы (поступны. вилимы) и определяются местом и спо-
собом их описания. Переменные. объявленные внугри функции. называют
локальными (автоматическими). область их вилимости — функцией. в KOTO-
рой они описаны. Переменные. объявленные вне всех функций, называют
глобальными, область их действия — все файлы программы.
Арифметические операции и выражения. Допустимые арифметические
операции для числовых переменных следующие: бинарные арифметические
операции (+. —. *. /). унарная операция (—, минус) и стандартные функции.
При делении (/) целых чисел дробная часть отбрасывается. Операпия леления
по молулю (9%) возврашает остаток от деления целых чисел и применяется
только к целым переменным.
Арифметические операции имеют приоритеты. перечисленные в порядке
уменьшения:
4—» — унарный минус:
a*», «/». «9» — (Мультипликативные) — умножение. деление, остаток
от деления пелых:
a+» — (аллитивные) — сложение, вычитание.
Операции увеличения «++» и уменьшения «— —» применяются в циклах
и будут рассмотрены далее.
1. Г. Тины данных и неременные. Сттруюнура нрогуаммы ма языке C++...
9
Выраженыя представляют собой правила получения новых значений. Вы-
ражения состоят из операнлов (констант, переменных. станпартных функций),
объединенных знаками операций и скобками в соответствии со своим прио-
ритетом. Элементы, из которых сконструировано выражение. характеризуются
своим значением и принадлежат к какому-либо типу ланных. Частный случай
выражения — олиночный элемент. т &. константа. переменная или обраше-
ние к функции. Тип значения выражения определяется типом операнлов и
випом примененных к ним операций. Правило использования операций с
операндами разных типов: при вычислении более короткие типы преобразу-
ются в более ллинные. те. можно сказать. что тип выражения определяется
наиболее точным (или более высоким) типом вхоляштих в него переменных.
Функцию можно рассматривать как простую переменную и использовать по
месту вызова. учитывая ее тип. Арифметические выражения — это константы,
переменные и функции. объединенные знаками арифметических операций.
Переменные. входящие в выражение. должны получить свое значение по их
использования в выражении.
Операция присваивания. Полученные при вычислении выражений значе-
ния должны сохраняться в области памяти и использоваться в пальнейпих
вычислениях. Для этой цели служит операция присваивания:
<«Г.-значение»=“В-значение»!
ТДЕ <Г-=значение>» — должно ссылаться на обьект. который может принимать
значения. т.е. это ссылка на некоторую область памяти: <Е-значение» —
любое выражение, имеющее значение.
В упрошенной форме с практической точки зрения операция присваи-
вания может быть записана так:
<переменная>-<«выражение>?;
Вычисляется выражение, его значение записывается в область памяти,
на которую ссылается <«1-значение». Старое значение этой области сти-
рается. В область памяги константы ничего записать нельзя. поэтому она не
является /-значением. Переменная является «1-эзначением». В программе
(C++ операции присваивания являются выражениями, поэтому можно ис-
пользовать многократное присваивание: c=f=b.
Переменная может получить значение как в результате присваивания,
так и заланием при вводе с клавиатуры или файла.
Ввод и вывод переменных. Ввод и вывод в C++ поддерживается двумя
системами: первая унаслелована от языка С. вторая осушествляется с помо-
шью потоков объектно-ориенгированной библиотеки С++. В консольном
режиме ввод данных осушествляется с клавиагуры, а ВЫВОД данных — на
экран. Ввод и вывод в системе. унаследованной от языка С, осуществляется
с помошью библиотечных функций станпартной библиотеки ввода/вывода.
Лля использования функции из стандартной библиотеки к программе необ-
холимо полеЕЛЮчЧИТЬ ЗАтоловочный файл stdic.h
finelude <stdic.h>
Форматированные ввод и вывод осушествляются функЦИЯМи scanf_s()
и printf (). Эти две функции: зсапЕ= () (для ввода) и printf () (для
10 Глава Г. Оспавные понятия C++. Алгаримы обработки простых последовательностней
вывода) выполняют преобразование числовых величин в символьное преп-
ставление и обратно. Прототипы функций scanf s{)H printt(}:
printi(<S0pmMatHan — управляющая строка>,<список аргументов»);
Вызовом функции реализуется вывод аргументов на консоль в соответ-
ствии с форматами. определенными в управляющей строке.
Управляющая строка состоит из спепификаторов форматов в соответствии
с типом выводимого аргумента. Число аргументов должно соответствовать
CHHCKY форматов. Управляющая строка содержит два типа объектов: обычные
символы, копируемые в выхопной поток, и спецификации преобразований.
вызываемые преобразование и печать очередного аргумента. Спецификация
преобразования начинается с символа +%» и заканчивается символом пре-
образования: $ «символ преобразования».
Основные спецификации преобразований пля вывола чисел:
+ /— аргумент, рассматриваемый как действительная переменная типа
float, преобразуется в десятичное число с плавающей точкой;
+ М— аргумент. рассматриваемый как действительная переменная типа
double:
« d — аргумент. рассматриваемый Kak целая переменная типа int, пре-
образуется к десятичному числу:
* § — спецификация преобразования пля вывола символов и строк: ар-
гумент полжен быть указателем на массив символов, который постаточен для
принятия строки и добавляемого в конце символа \.0: аргумент преобразуется
в строку;
* с — спецификация преобразования для вывода символов. аргумент —
отдельный символ:
. р — аргумент, рассматриваемый как указатель, преобразуется в адрес.
Примеры:
printt (“ввод x:\n"); //вывод текста
Вывод переменных int Е, i; float x;
fin
у:
printt (“\n 1=$%9 КЕЗЯ х=8
peeЕже
Форматный ввод-функция scant s{) является аналогом printt():
зсапЕ з {<управляющая строка», список аргументов»);
Вызовом функции реализуется чтение данных. вводимых с клавиатуры.
Результаты помешаются в аргументы, которые интерпретируются в соответ-
ствии с форматом. указанным в управляющей строке. Управляющая строка
содержит спецификации преобразования. применяемые для интерпретации
входных последовательностей. Символ преобразования опрелеляет интер-
претацию поля ввода: + <спецификатор». Каждый аргумент — указатель
на переменную типа. указанного в спепификации преобразования. Число
аргументов должно соответствовать числу спепификаций. Аргументы —
адреса переменных.
1.2. Логические выражения. Разаеталяющиеся алесриитмы. Условный оператор
и
Примеры:
* ВВОД ПЕеременных. описанных Kak int k; float x;
scant в (“td %£”, &Е, Ех);
* ввод переменных, описанных Kak float xl, yl, x4, у2;
printé ("\n Введите координаты точки \п”);
scant 5 ("32% Е%Е%Е”", &х1, &yl, 5х7, &у2);
* ЛЛЯ ЕВОЛа И БЫБОДа СИМВОЛОВ char ch и стро пелесобразно исполь-
зовать специальные функции, их прототипы:
*int getchar(void}), int putchar(int);
“char “ getoaf{char No och], int puts(char * ch);
ch=getchar {); putchar (ch);
Линейный алгоритм. Это алгоритм. представляющий собой послелователь-
ное выполнение действий. В линейной программе операторы выполняются
послеповательно. Реализуются операторами ввода, вывода и выражениями.
Например: вычисление полупериметра треугольника:
finclude <stdio.h>
woid main{) {
float a, b, с, p Я; // описания переменных
secant з{”%Е %Е %Е”, а, «hb, &с\;//ввод коэффициентов
printi("\n а=%10.3Е b=3f с=%Е \п”, а, b, с);
p=(atbte) /2;
printE("\n р=%10.3Е \n", р);
зсапЕ s("tiv, ва ); // задержка
1.2. Логические выражения. Разветвляющиеся алгоритмы.
Условный оператор
Логические (условные) выражения, которые используют в конструкци-
ях языка C++, реализуются с помошью операций отношения и логических
операций.
Логические выражения. В языке C++ любое ненулевое значение интерпре-
тируется как истина, а НУЛЬ — Kak ложное значение. Это значение может быть
значением арифметического типа (целое или вещественное) или указателем.
Вязыке (C++ также используется логический тип данных bool и логические
константы true, false. Любое ненулевое значение в логическом выраже-
нии преобразуется в true и наоборот: нуль автоматически преобразуется в
false, а все ненулевые значения — в true.
Можно выделить логические выражения трех типов: операнлы, связан-
ные знаками операций отношения (в частом случае — арифметические вы-
ражения): логические переменные, связанные знаками логических операций:
объединение первых двух типов.
12 Гаана |. Основные понятия C++. Алгоритмы обработки простых nocwedoramernocmed
Онераныи отношения:
>= >, <=, < — больше или рано. больше, меньше или равно. меньше:
== != — равно. не раено.
Операции >=, >, <=, < имеют одинаковый приоритет, он вЫше, чем
приоритет операций ==. != _ Однако их приоритет ниже приоритета ариф-
метических операций: в выражениях типа /< н- 1 скобки не нужны и они
понимаются Kak /< (н- 1).
Логические операции:
| ~ — логическое отрицание «НЕ».
&& — логическое «И»:
| — логическое «ИЛИ».
Логическое отрицание ('!) дает в результате значение 0, если операнд есть
истина (не нуль). и значение 1, если операнд равен нулю (ложь). Операнд
должен быть логического или арифметического типа (целого или вешествен-
ного) либо указателем. Унарная операция отрицания (!) преобразует значение
true B false и наоборот.
Логические операции 44: и || объелиняют выражения отношений в соот-
BEICIBHH С Правилами пля логических «И» и «ИЛИ». Выражения. связанные
операциями dd: и |. вычисляются слева направо. их вычисление прекрапта-
ется по лостижении результата {истина или ложь).
Выражение £1 && Е2 £4 .. En — кОНЬЮНЕПИЯ ЛОГИЧеСских перемен-
ных) — истинно. если истинны все логические переменные (аналог
умножения).
Выражение 21 || Е2 || . || Ел — дизьюнкция логических перемен-
ных) — истинно, если истинна хотя бы одна переменная (аналог сложения).
Выражения, связанные операциями && и ||. вычисляются слева направо.
Приоритет операции && выше. чем операции ||, и у обеих операций приори-
тет ниже. чем у операций отношения. Унарная операция отрицания (!) имеет
самый высокий приоритет.
Базовый пример: / — целая переменная, которой присваивается значение
логического выражения.
Проверка целого число м на четность:
ги
jon%2>=-0;илиj=!{n4%2).
Проверка целого число л на кратность 3 или 5:
= %3==0Пп%5==0;
ии=(n&3)ПГ5.
Проверка принадлежности переменной х одному из заданных интервалов,
расстановка скобок определяются приоритетами операций:
j==> 10 ce х<50
|x>100ЕЁхх150;
Разветвляющиеся алгоритмы. Условный оператор. Развемеляющыеся
Элгорытмы — такие алгоритмы, в которых в зависимости от выполнения
определенных условий осуществляется выбор одной из М альтернативных
ветвей вычисления. Для реализации разветвляюлтихся алгоритмов в С++
применяется условный оператор. Выбор из лвух ветвей вычислений, или
развилка, осуществляется базовым оператором:
12 Логические выра мсеныя. Разаета ьающиеся Загорымтмы. Условный omepamop 13
«нолных условным оператор> :=
if (<выражение>) <оператср1>;
else <оператор2>!;
Операторы <оператср1> и <оператер2> могут быть простыми или
составными Оператор =] зе является расширением оператора if. Сначала
вычисляется выражение в скобках В C++ результат условного выражения —
скаляр. который может иметь числовой или символьный тип, указатель или
тип bool. Нулевое значение дает значение «ложь», ненулевое — значение «ис-
тина». Тип выражения необязательно ограничивать операторами отношений
и логическими операторами или операнлами типа bool. Необхолимо только,
чтобы результат вычисления
условного выражения
можно было интерпрети-
ровать как значение «истина» Или «Ложь».
В зависимости от значения выражения выполняется опна из ветвей вычис-
ления Если выражение
истинно (ненулевое), то выполняется <соператср!>,
если ложно — выполняется альтернативное лействие в <операторе2>, после
чего выполняется слелукиций оператор.
Выбор максимального (тах) значения из двух чисел а и В, используется
полный условный оператор:
if (в > B) max=a; else мах=Ь;
Развилку одной из двух ветвей вычислений можно реализовать лвумя
сокрашенными условными операторами (ветвь =] зе отсутствует):
«сокращенный условный сператор>::=
if (<выражение> ) «оператор»;
Если выражение истинно, то оператор выполняется, если ложно — про-
пускается Для выбора из двух ветвей вычислений используются два сокра-
шенных условных оператора: логическое выражение для выполнения альтер-
нативной ветви препставляет собой отрицание логического выражения для
выполнения оператора другой ветви.
Решение предылущей задачи выбора максимального (шах) из двух чисел
аи Вс использованием IBVX сокрашенных операторов:
if(в>b)max=a;if(b>а)max=b;
Для объединения нескольких операторов в один служит составной опера-
тор: группа операторов, заключенная в операторные скобки {}. После блока
точка с запятой не ставится. Каждый оператор, в свою очередь, может быть
составным. Если в ветви вычислений необходимо выполнить несколько дей-
ствий. используется составной оператор
if <выражение>»
{оператор1; оператор 2;..<последовательность
операторов>;..< оператор n>; }
else
{<оператор п+1>;..<последовательность
операторов»;..<оператор n+m>; }
14 frded |. Основные nonamus C++. Алгоршитмы обрабогнкы просттьсе moceedoddimemxaocmed
Многовариантный выбор. ia реализации выбора из М альтернатив при-
меняются вложенные условные операторы Выбирая в качестве оператора
действия другой условный оператор. можно вклапывать олну в пругую не-
сколько конструкций if =1зе. реализуя многовариантный выбор. При
записи вложения выполняются отступы. Логичнее вложение осушествлять
в ветви =1зе. Если выбор осушествляется из трех альтернатив. в качестве
<осператора?2> используется также условный оператор:
if (<высажение>)
<onepatopl>;
Выбор из No альтернатив осуществляется послелукущим вложением услов-
ного оператора в ветвь =1зе вложенного условного оператора и т л. После
любого оператора if можно поставить оператор =1зе лля выполнения аль-
тернативного действия: в этом случае =] зе связывается с ближайшим прелы-
пушим if, не содержашим =] se. Такая конструкдия называется в программе
на языке C++ «цепочка if — then — =lses. Усповия послеловательно вы-
числяюлся сверху вниз до первого выражения. значение которого истинно.
В этом случае выполняется связанный с этим условием оператор. Если все
условные выражения ложны, выполняется оператор в послелней ветви e152.
При выполнении вложенного условного оператора вычисляется одна из
ветвей. Для получения требуемого соответствия в альтернативных случаях
необхолимо применять фигурные скобки.
Например. вычисление кусочно-заданной функции в интервалах:
для а <x < В рассматривается функцияу = x - x,
для x > в функция В рассматривается у = —2х.
Решение с использованием полного условного оператора:
i=(x<a)ЕН!
else
if (x <= b) ЕЕн*н;
else
z=-2*u;
Многовариантный выбор альтернативно можно реализовать с помошью
набора сокрашенных условных операторов:
если <условие>», то <действие»;
В каждом из операторов условное выражение определяет условия вы-
полнения данной ветви. В этом случае выполняется один из М сокрашенных
условных операторов. Решение предылущей запачи с использованием трех
сокрашенных условных операторов:
if(x<=а!z=x;
if(x>=a&&x<=b)z=x“x;
if (x > b) 2=-2*x;
1.2. Логические выразжеения. Разветалякнициеся алеоритнмы. Меловный оператор?
15
Многовариантный выбор можно также реализовать оператором switch
(переключатель). в котором тип значения условного выражения — целый
ИЛИ СИМЕОЛЬНЫЙ:
switch (выражение) {
Case «константное выражение 1>:
[последсвательность операторов]
Case «константное выражение 2%:
[послеловательность операторов]
сазе «константное выражение n>:
[последовательность операторов]
[default: сператоры]
Выполнение оператора начинается с вычисления переключающего вы-
ражения, а затем управление передается первому оператору из списка case,
значение константного выражения которого совпало с вычисленным значе-
нием. Выход из переключателя обычно выполняется с помошью операторов
break
ИЛИ return.
Например. в зависимости от значения пелой переменной вычисляются
разные значения функции:
switch (k}
{ case 1: you; break;
case 2: yoxu*x; break;
case 3: yo-2*x; break;
default: у=0;
Решение залач. Пример Г. Положение лвух прямых у = Ах +2, заланных
своими коэффициентами (, В: k,, В.. К.. В,. Для решения используется вло-
женный условный оператор с тремя ветвями вычислений. Точка пересечения
двух прямых опрелеляется из условия равенства координат у Е этой точке:
ках + В, = k,x + b,. Отсюда определяются координаты точки пересечения хр,
yp:xp=(b,—by)ky—К);ур=Kx+by
Код главной функции:
void main() {
intl, bl, ka; 62: floak.zpy ур;
printf ("\n Введите коэффициенты прямых \п”);
scant s("sfsfs£tf", «kl, ebl, Ка, 662}:
if (kl == k2 && b1l==b2)
printf(*” \n прямые совпадают \п”);
else
if (kl == k2) then
printi("\n прямые параллельны \n”);
else |
up=(b1-b2)/ (k2-k1);
ye= kl*x+bi;
16 Глава Г. Оспавные понятия C++. Алгариммы обработки простых последовательностней
printi(” хрЕЗФЕ ур=%Е Mees ape Уря
if (kl == -(1/k2)}}
printf ("\n прямые перпендикулярны \n");
Пример 2. Решение квапратного уравнения ах’ + bx + ¢ = 0.
В алгоритме используется выбор из пяти ветвей вычислений. Код прог-
раммы:
#include <stdio.h>
#ineclude <math.h>
void main() {
float xl, xZ, a, b, с, Я; // описания переменных
scanfi("$f£$f4i", ga,ab,éc); //ввод коэффициентов
а=%10.3Е b=%F с=%Е \n", a, b, с):
Г
\n уравнение линейное а=0 \n");
и
{
РАН Еfb;
weantt М1 ЕВЕ
ae
printt("x1-ti", x1);
}
else
printi("” решения нет \n");7}
\Г
else
{
d=b*b-4*arc; // дискриминант
Е
>О)
{
xl=(-bt+sqrt (d))/(2%a);
uZ=(-b-sqrt (d))/ (2%a); // корни
z=a*xl*xltb*xlt+c; // проверка
else printf(" complex \п”);
1.3. Итерационные циклы. Операторы цикла
Циклический алгоритм — алгоритм, в котором необхопимо повторять за-
данную последовательность действий при выполнении определенного усло-
вия. Переменные, изменяющиеся в теле цикла и используемые при проверке
условия продолжения, называются параметрами дикла. Любой цикл состоит
из тела цикла, т.е. операторов, которые выполняются несколько раз, началь-
ных установок, модификации параметра цикла и проверки условия пролол-
жения выполнения цикла. Опнократный прохол цикла называется мнерацией.
До входа в HET необходимо задать начальные значения переменных,
вхоляптих в цикл, и параметров цикла (переменных. изменяющихся в теле
1.1. Итеранционные циклы. Операторы цикла
17
цикла и используемых в выражении, которое опрелеляет условие выполнения
цикла). Цикл завершается, если условие его прололжения не выполняется.
Проверка условия осуществляется на каждой итерации либо до тела цикла
(тогда говорят о цикле с прелусловием), либо после тела цикла (цикл с пост-
условием}. Тело цикла с постусловием всегла выполняется хотя бы один раз.
Целочисленные параметры цикла. изменяющиеся с постоянным шагом
на каждой итерации, называются снемниками цикла.
Операторы цикла (while, do/while и for) служат для организации
многократно повторяющихся вычислений. Выделяют циклы с неизвестным
числом повторений (циклы, используемые пля решения поисковых задач) и
циклы с известным числом повторений (счетные циклы). Циклы с неизвест-
ным числом повторений, выход из которых осуществляется при выполнении
заланных условий, называются имеранионными.
Используя базовые структуры (следование, ветвление и цикл «пока»),
можно создать программную реализацию любого алгоритма. В противопо-
ложность циклам for и while, сначала проверяющим условие, цикл do/
while проверяет условие в конце, т. е. цикл do/ while всегда выполняется
по крайней мере один раз.
Для принудительного завершения текущей итерации и цикла в целом
служат операторы break и continue. Оператор break используется внутри
операторов цикла или switch — для обеспечения перехода в точку програм-
мы, находящуюся непосредственно за оператором, внутри которого имеется
оператор break. Оператор continue (оператор перехода к следующей ите-
рации цикла) пропускает все операторы, оставшиеся до конца тела цикла, и
перелает управление на начало следующей итерации.
Цикл «пока» (while). Базовая структура в программировании цикличес-
ких алгоритмов во всех языках — цикл «Пока»: пока логическое выражение (ус-
ловие) истинно, выполнять оператор. Определение цикла «пока» (while)pCt++:
«цикл «0onax>:i:cwhile («условное выражение») <onepatop>;
гле в общем случае <onepatop> — это блок или составной оператор .
Выражение определяет условия выполнения тела цикла. Тип выраже-
ния — числовой, указатель или логический. Выполнение оператора цикла
while происходит таким образом: если условное выражение истинно, т. е.
его значение не равно нулю, тело цикла выполняется. Цикл «пока» — циклс
предусловием. Он может ни разу не выполниться, если условие перед входом
в цикл ложно, т. е. его значение равно нулю.
Цикл с постусловием (do/while). Он выполняется до тех пор. пока ус-
ловие не станет ложным. Цикл do/ while проверяет условие в конце, т. е. он
всегда выполняется, по крайней мере, один раз. Определение цикла de/ while:
do{
<оператор?;
} owhile (условие);
Сначала выполняется простой или составной оператор, составляющий
тело цикла. а затем вычисляется выражение. Если оно истинно (не равно
false), тело цикла выполняется еще раз. Цикл завершается, когла выражение
18 Глава Г. Основные поняния C++. Лагариамы обработки pec послебовагельносней
станет равным false или в теле цикла будет выполнен какой-либо оператор
передачи управления.
Пример программы проверки ввода: оба цикла (while wuda/while)
предназначены для организации циклов с неизвестным числом повторе-
ний, а выход из цикла осуществляется по выполнению некоторого условия.
В качестве параметров циклов можно использовать несколько переменных,
вхоляших в заланные выражения и связанных знаками операций отношения
и логических операций, что позволяет осушествить проверку сложных усло-
вий. Когда условие становится ложным, выполняется следующий за циклом
оператор.
Илиомы программирования. В залачах обработки числовых послелова-
тельностей базовыми алгоритмами являются следующие: вычисление сумм,
произведений, средних арифметических значений, поиск экстремумов. Для
вычисления сумм и произведений применяются рекуррентные соотношения,
в которых текущее значение вычисляется через прелылущее, текущее сохра-
няется, а предыдущее «теряется». В общем случае запись имеет вид
«переменная? -<‹переменная>х «операция» «<модифиватор у;
где под знаком «операция» рассматривается любая арифметическая
операция.
Для изменения значения переменной из области памяти извлекается
значение, модифицируется и записывается в ту же область памяти, прежнее
значение стирается (применяется в циклах для вычисления сумм. произве-
дений, накопления счетчиков). Для записи таких выражений в программе на
языке С++ используются сокращенные операции присваивания:
а
a
что позволяет свести символическую запись выражения
<переменная>-‹переменная» «‹операция> «молификатор?;
к более короткой записи:
«переменная» «операция>=<«модибикатор>.
Базовые примеры вычисления сумм и произведений:
п+=2;
sta;
р*=а;
Для изменения целой переменой на 1 в С++ существуют лве операции
{инкрементация H декрементация) — операции увеличения и уменьшения
значений целых переменных. Операция УВЕЛИЧения « t+» добавляет | к CBO-
ему операнду, а операция уменьшения «— —» вычитает 1. Операции увели-
чения и уменьшения можно использовать либо как префиксные операции:
операции увеличения «+ +» или уменьшения перед переменной (++Л. либо
как постфиксные знаки операций после переменной (/+ +). Выражение ++/
увеличивает переменную / до использования ее значения, jt t увеличивает
переменную / после того. как ее значение было использовано.
Операции увеличения и уменьшения можно применять только к целым
переменным. Идиомы рекуррентных соотношений вычисления сумм, нако-
пления счетчиков, произведений:
1.3. Ишеранионные циклы. Операторы цикла
19
1++; // увеличение;
i--; // уменьшение;
з+=а; // сумма;
2“=а; // произведение.
Начальные значения: ==0; р=1; 1=0; р=1;
Операции. расположенные в одной строке, имеют один и тот же уровень
старшинства: строки расположены в порядке убывания старшинства.
Знак операции:
Порядок выполнения:
| вой)
справа налево
*/%
слева направо
+-
слева направо
<<¢= > >=
слева направо
== [=
слева направо
ded
слева направо
|
слева направо
= += -= *=./= итд.
справа налево
Приемы программирования обработки последовательностей. Для вычис-
ления средних, заланных некоторым критерием отбора, необходимо в теле
цикла использовать сокращенный условный оператор для задания критерия
и вычислять суммы (или произведения) заданных элементов и их количество
{счетчики ):
if {‹условное выражение - критерий») {в+=а; 1++;|
Алгоритм поиска экстремумов: задаются начальные значения максимума в
переменной max (минимального — в переменной min), равные первому числу,
Hero номер imax=0 (минимального — imin=0). Далее в пикле текущее зна-
чение сравнивается с экстремумом; если текущее значение больше текущего
экстремума, то экстремум и его номер изменяются на текущие значения:
if («текущее значение» > max)
{ MAMNS=<TenyMmee значениеь;
imax-<HomMep значениях; }
Для поиска в последовательности заланных элементов (образца) на каж-
дом шаге цикла используется условный оператор пля задания критерия; если
условие выполняется, то переменная «счетчик вхождений» увеличивается:
if {чтекущий>» == <«образцу>)
itt:
Решение задач на циклы с неизвестным числом повторений. Для после-
ловательности целых чисел до первого отрицательного необходимо найти
среднее значение и количество всех чисел. кратных 3 или 5: произведение
четных чисел: максимальное число и его номер.
Код программы:
#include <stdio.h>
woid main(void) {
20 Глава J. Основные поняния C++. Алгоритмы обработки простых послебовагельносней
Teng int.i, 1, Е mm ny 9, imax, max, imin, min;
double р, а, wy
scanf s("td", en} ;
max=n; min=n; imax=imin=0; „/начальные значения
while{mn>0)|
if ({'({m %2))
p*=n; // произведение четных
if(183=0|!(n95))
{st=n; itt+;} // сумма m количество
if(п>max)
{max=n; imax=i;}
// максимальный злемент и его и номер
Cinten; // ввод следующего
if (i) в /= 1; // среднее, если количество не равно 0
Выделение цифр целых чисел. Алгоритм выделения цифр целого числа:
пока М <> 0 выполнить!
выделить последнюю пифру числа: Е = М *% 10;
понизить разряд числа: М = No10;
конец пока.
Формирование чисел по цифрам в примом и обратном порядке. В алгорит-
ме используется решение предыдущей задачи выделения цифр числа п. Для
формирования числа по цифрам в прямом порядке в переменной по: ис-
пользуется рекуррентное соотношение
nprSnprt+k*i;
где К — текущая цифра числа п; 1 — вес цифры в записи числа npr,
1=1%10; 1=1%10 —
увеличение веса разряла текущей цифры k в числе npr Этот алгоритм по-
зволяет решить залачи удаления заданных цифр или заданной послелова-
тельности цифр.
Для формирования числа по цифрам в обратном порядке в переменной
nobzr используется рекуррентное соотношение
nobr=nobr*10+k;
где Е — текущая цифра числа п.
На каждом шаге цикла текущая цифра К увеличивается в разряде К. Код
программной реализации рассмотренных задач:
void main(){
Leng Пет, 1; Е пм, 1, вое; nobr:
scant 8{”“%9”, &п); ff n=28260754; задание константой
j=1;
// счётчик разрядов
npr=0;
nebr=0; // начальные значения
while {[n}
{
f/f или {n!= 0}
k=n310; // цифра
п /=10; /’ понижение разряда
1.4. Организания счетных ы итерационных MeO
21
// удаление заданных цифр из записи числа}
afakeNoЖЕЕи!7afПРЕ.10
}
nebr=nobr*10+k; // чысло из цифр в обратном порядке
}
cout << npr <=”
"сх nobr << endl; }
1.4. Организация счетных и итерационных циклов
Оператор for в языке С++ обеспечивает высокую эффективность по
сравнению с другими языками и позволяет организовывать как счетные, так
и итерационные циклы. вводя в условие выполнения необходимое количество
параметров и используя сложные логические (условные) выражения. Цикл с
параметром for реализован как цикл с предусловием:
for (<инициализация»; «условие>; «модификация») <onepatop>;
Цикл for работает до тех пор, пока условие истинно. Когда условие ста-
новится ложным, выполнение программы продолжается, начиная с оператора.
следующего за циклом for. Оператор for имеет три главные части, которые
следует разделять точкой с запятой. В части инициализации обычно находятся
операторы присваивания, используемые пля установки начальных значений
переменных цикла. Инициализация служит для присваивания начальных
значений величинам, используемым в цикле: записываются операторы при-
сваивания, применяемые для установки начальных значений.
В этой части можно записать несколько операторов. разделенных опе-
ратором последовательного выполнения или оператором «запятая». Иници-
ализация выполняется один раз в начале исполнения цикла.
Условие — логическое (условное) выражение, аналогичное условному вы-
ражению циклов while и do/while и определяющее условие выполнения
цикла. Цикл выполняется до тех пор, пока значение этого выражения остается
истинным. Выражение вычисляется перед каждой итерацией.
Молификации выполняются после каждой итерации цикла и служат
для изменения параметров цикла. В части молификаций можно записать
несколько операторов через запятую. В качестве параметра цикла можно
использовать любую простую переменную (действительное. целое, символ)
или указатель, шаг изменения параметра может быть любым.
Тело цикла представляет собой простой (или составной) оператор. При-
нулительное завершение такого цикла может быть организовано с помошью
оператора break. Любая из частей оператора for может быть опущена. но
точки с запятой остаются. Пример бесконечного цикла:
for{;;)
printf£(” for\n”).
Оператор цикла fcr позволяет организовать как счетные циклы, так и
циклы с неизвестным числом повторений. Параметром цикла может быть
переменная любого базового типа. Шаг изменения параметра — любой.
22 (raed /. Основные понятия C++. Аагоритмы обработки прослных последовагнельностней
Примеры счетных циклов: вычисление суммы чисел, факториалов. вы-
числение степени некоторого числа х:
Foid main{}{
double =, x, Wr
Long. sete 2s aig: а
cin >> п!
for (i=1, в=0; 1<=100; 1++) в += i;
for (1=1, wel; isn; м “= i, itt); // вычисление x
Eor {i=0, м=1, i<n; itt) wt=x; // вычисление x?
Табуляция функции — вычисление функции в узлах равномерной сетки.
Пример решения залачи табуляции заланной функции и вычисление сред-
него и экстремума с использованием оператора for. В части инициализации
и модификации используется операция запятая:
#include<stdic.h>
#include«<math.h>
void main{){
float a,b,x,s,h,y,sr—0,c,
min, xmin;
int i,7-0,n;
scant s("Sf£tf td”, é&a, &b, én);
for({i=0, 9=0, sr=0, x=a, h=[{b-a)/n, min=sin(a)*a, xmin=a;
чер itt+;)}{
yosin
(x) *x;
if{y<-min ){ min-sin(x)*«, xmin=x }
if{y>O){
srt—y;
j++;
}
printf ("\n x=Sf
yori", х, у);
ЕНЕН;
== (520) зк=вх/З;
printf {“\п Srednee znachenie ye0=tf", sr);
printf ("“\n min=%f£", min);
Цикл fer, используемый для решения залачи с неизвестным числом по-
вторений. показан на примере выделения цифр целого числа и определения
числа разрядов:
Foid main(){
int S57 ,ke ne
scanE s[{"%d", én) ;
for {j=1; м
ЛЕНЕ
k=n410; // цифра
п /=10; // понижение разряда
1.6. Заааныя dis cawocmeme
ne eno en
b
a
L
a
d
1.5. Принципы структурного программирования
Решение любой задачи в программировании
можно реализовать с ис-
пользованием трех базовых конструкций: следование, развилка (выбор),
повторение (цикл). а также их комбинаций Любая из базовых конструкций
имеет только олин вхол и один выход, что позволяет реализовать их произ-
вольное вложение прут в друга.
Следование — последовательное выполнение действий. Использование
этой конструкции позволяет реализовать линейные алгоритмы.
Развилка (выбор) — выполнение одного из альтернативных вариантов
действийв зависимости от соблюдения условия. Эта конструкция позволяет
реапизовать разветвлянутие алгоритмы.
Повторение (цикл) — многократное выполнение определенной последо-
вательности действий. Конструкции применяют для реализации циклических
алгоритмов.
Построение структурированных программ основано на использовании
лвух правил: суперпозиции и вложения.
Суперпозиция заключается в последовательном размешении управляющих
структур. когла точка выхода олной из них соелиняется с точкой вхола другой.
Правило вложения состоит в слелующем: любую управляющую струк-
туру можно заменить любой пругой управляющей структурой. Эти правила
не попускают их частичного перекрытия, что противоречило бы принципам
СТруЕТУрНОГО ПРОГраммирования.
Из принципа структурного программирования слелует, что можно выде-
лить три управляющие конструкции языка (операторы языка): следование,
выбор, повторение. Выделяют следующие основные категории: оператор-вы-
ражение. оператор ветвления, оператор цикла, блоки. Точка с запятой — при-
знаЕ конца оператора. Рассмотрим операторы языка C++ для реализации
приведенных базовых конструкций.
Оператор-выражение — попустимое выражение языка, завершающееся
точкой с запятой. К операторам ветвления относятся условный оператор if
и оператор переключатель switch. К операторам цикла относятся операторы
while, for, do/while.
Фигурные скобки { } используют для объединения описаний и операто-
ров в составной оператор или блок, так что они оказываются синтаксически
эквивалентными одному оператору (например, фигурные скобки, в которые
заключаются операторы, составляющие функцию). Точку с запятой не ставят
после фигурной скобки, завершающей блок.
1.6. Задания для самостоятельного выполнения
Залание 1. Задано множество точек из первой полуплоскости. Вычислить
срелнее значение расстояния от точек до начала координат и определить их
количество.
24 Глава 7. Осповные noxamua С+-—_ Alzopumu ofpodomicu mpocmat поводы nocmell
Задание 2. Залана олна окружность с коорлинатами
пентра VY. У и рали-
yoom А. Для множества точекиз левой полуплоскости проверить попалание
точкив окружностьи опрелелить количество попаланий.
Залание3. Заланы лва пелых числа Вылеляя пифры этих чисел. сфор-
мировать новое число. выбирая в качестве цифры меньшую из выделенных,
определить количество разрядов нового числа.
Залание 4. Вычислить значения функции в узлах равномерной сетки
у = xsin (x) на заланном интервале а. 4 лля заланного множества точек М,
найти максимальное
и минимальное значения функции и соответствующие
значениях.
Залание 5. Задано множество точек и олна прямая с коэффициентамиА. В.
Вычислить количество точек выше и ниже прямой _
Залание 6. Залано целое число A. Вылеляя цифры. проверить естьли в
записи числа Ibe подрял идушие одинаковые дифры.
Глава 2. СПОСОБЫ ПРЕДСТАВЛЕНИЯ СТРУКТУР ДАННЫХ.
СОСТАВНЫЕ ТИПЫ
Переменную составного типа можно рассматривать как структурную
переменную. которая представляет собой множество величин, семантически
связанных межлу собой (векторы, матрицы, структуры), т. е. как совокуп-
ность других переменных — структурированных или простых. Память под
них вылеляется в последовательных участках памяти, которым присваивается
олно имя (имя переменной). Количество байтов, выделяемых под кажлую
компоненте определяется ее типом.
Для прелставления переменных, состояших из других компонент, можно
использовать иерархическую структуру дерева, компоненты кажлого уровня
которого мотут принаплежать только к олному типу Уровень листьев дерева
составляют отлельные скалярные (простые) компоненты, которым могут при-
сваиваться значения,т е. распространение значений осуществляется
от листьев
перева Е верхним уровням. В группу составных типов вхолят массивы, структу-
ры. указатели. ссылки. перечисления, классы. Создание описаний составных
(пользовательских) типов выполняется на основе базовых и сушествующих
типов для представления структурных переменных. Простейший составной
тип — массив, который строится из базовых типов, определенных в языке.
По способу хранения переменные в языке C++ подразделяют на стати-
ческие, автоматические и динамические.
Статические переменные — все глобальные и локальные объекты (пере-
менные), описанные с модификатором static. Они создаются в статической
памяти и хранятся в ней до окончания работы программы. Локальные пере-
менные без молификатора static и аргументы функций являются автомати-
ческими объектами. Они хранятся в автоматической памяти — программном
стеке. Стек создается в момент начала работы программы. При входе в блок
в стеке выделяется память для всех автоматических объектов этого блока,
при выходе из блока эта память освобождается.
Олин из способов решения заплач обработки больших массивов данных и
адаптации памяти к объему данных — использование динамической памяти и
пинамических переменных, память пля которых вылеляется по требованию во
время выполнения программы и освобождается по требованию специальным
диспетчером динамической памяти по определенному запросу. Эта память
находится в куче — динамической области памяти, расположенной между
сегментами кода программы, сегментами данных и стеком.
Память пол динамические массивы выделяется по требованию в процессе
выполнения программы. Созпание таких массивов реализуется операцией new.
26
Глава 2 Способы предстаазления структур данных. Состеданые mur
При динамическом связывании вылеляются неименованные области памяти
в куче пля хранения ланных Доступ
к области. созланной
по принципу ли-
намического связывания. осушествляется посрелством указателей Сначала
рассматриваются алгоритмы обработки числовых массивов. в лальнейнтем —
массивы структур и экземпляров классов.
2.1. Статические массивы. Статическое связывание
Массив — упорялоченный набор компонент олното типа. семантически
связанных межлу собой Массив характеризуется
своим именем. типом комто-
нент. числом хранимых компоненги их нумерацией. Память пол компоненты
массива выделяется в последовательных участках памяти. которым присваи-
вается одно имя — имя переменной массива Количество байтов. вылеляемых
под каждую компоненту определяется ее типом Распределение памяти под
массив во время компиляции называется сматическим связыванием: простран-
ство под массив вылеляется олин раз и не может быть изменено в процессе
выполнения. Это требует вылеления максимально возможной памяти пля
решения разных задач: возможно созланные массивы не понадобятся или
выделяемая память намного превыптает требуемую.
Сначала массив необходимо объявить. т е. выделить участок памяти
под запанное число компонент Для объявления необхолимо определить три
части: тип значений компонента. имя массива. количество компонент Объ-
явление статического одномерного массива. память пол который выделяется
на этапе компиляции:
<тип компонента> < идлентибикатор >[<число компонент)];
Размер массива фиксирован на этапе компипяции. Примеры описаний
одномерных массивов:
const int nmax=200;
int а[100], b
100]; int at[mmeax]; float bt [mmax];
Объявление массива позволяет выделить необходимый объем памяти.
Под массив из и компонент память выделяется последовательно пол каждую
компоненту: л последовательных участков из М байт Число М опрелеляет-
ся типом компонента: функция sizeof (<тип>) возврашает количество
байтов в памяти, выделяемых под переменные типа. В общем случае под
массив из п элементов выделяется н участков из sizeof (<тип>) байтов:
n*sizeot (<тип>)} байтов. Этому участку памяти дается одно имя. доступ
к отдельной компоненте определяется по его номеру (или по индексу). Ну-
мерапия компонент осуществляется с нуля. Элемент массива — это участок
памяти, для его выбора необходимо указать ето номер или индекс. Доступ к
элементу массива:
< идентификатор массива >[<индекс»]
где <индекс> выражение целого типа:
21. Статические массивы. Статыческое саязытание
и
a[O], ali], afl4*i]), а[2%1+1].
Нумерация в языке C++ начинается от 0. Допустимые значения индексов
нахолятся в пиапазоне от нулевого ло конечного значения, равного «число
компонент» - 1: значения инпексов He должны выходить за указанные
транипы.
После объявления массива необходимо запать значения элементов и пля
проверки вывести его на экран.
Принципы потокового ввола/вывола в С++. Залание значений элементов
массива. В C++ для ввода/вывода используются потоки.
Поток (stream) можно рассматривать как логическое устройство, по-
лучающее или перелающее информацию. Операции ввопа/вывола осушест-
вляются с помошью классов isfream (потоковый ввол) и osfream (потоковый
BHEOL), Произволный класс jostream — лвунаправленный ввод/вывод. Поток
представляет собой объект потокового класса. В библиотеке юятеат.й опре-
делены стандартные потоки: см — объект класса fsiream, соответствующий
стандартному вводу, сон! — объект класса osiream, соответствующий стан-
дартному выволу Для использования в программе библиотека полключается
заголовочным файлом:
finclude <iostream>
В качестве разделителей значений в потоке применяют пробельные сим-
BOB, знак табуляции, перевол строки. В потоковые классы включены опе-
раторы лобавления данных в ПотоЕ << и извлечения ланных из потока >>.
Каждый оператор «указывает» в сторону перемешения данных:
>> — перемешает данные из потока в переменную,
<< — перемещает данные из переменной в поток.
Извлечение данных из потока:
cin >> «идентификатор переменной базового типаъ!
cin>>=HNo=!
THe x, = — переменные базового типа (числовые или символьные. в которые
помешаются данные из потока cin).
Добавление ланных в поток:
+
cout << «идентификатор переменной базового типа» [<строко-
ая константа»];
Вывод на зкран осуществляется в формате. заланном по умолчанию,
endl — манипулятор перевода курсора на новую строку.
Например:
cout<<=<<“g="«<Е<<endl;
Элементы, объявленные в библиотеке языка С++. относятся к простран-
CIBY HMeH std. Доступ в членам пространства имен std извне возможен
двумя способами: явное указание пространства имен или оператора разре-
шения области випимости:
std:: cout << " каски;
std:: cin >> x;
Включение пространства имен в область вилимости Ha внешнем уровне
или текушего блока реализуется предложением
78
Глава 2. Снособы npedemaaienia crpyanyp данных. Cocuraaiive мыны
using папеврасе «имя пространства именъ;
using namespace std;
В этом случае обращение к членам пространства выполняется без ука-
зания имени пространства:
cout <“
ый
cin >> к;
Обработка массивов выполняется операторами циклов, которые задают
инлексы. Фактическое количество элементов больше нуля и меньше коли-
чества участков памяти. Залание значений элементов массива может быть
выполнено с использованием слелующих способов:
* ввод с клавиатуры,
* чтение из массива констант. моделирование.
* формирование из элементов других массивов:
* задание случайными числами.
Цикл заполнения массива:
for (int 21=090: 2. < np itt}
std::cin>>alil];
Базовая заготовка программы составляется как шаблон для задания и
обработки статических массивов. Рассматриваются разные варианты зада-
HHA массива:
#include <iostream>
#include <math.h>
using namespace std;
const int nmax-150;
Foid main{}{
int xuc[]={1l, 2; 53, 4, 35; 16, Te-12};
// массив констант
int. 1, Е mp jy п, id, but, na, пе, Е, wax, imax;
// описание целых переменных
float sum, в, 31, bufl; // описание действительных переменнных
int c[nmax],
int а[100], b[100]; //объявление массивов
//ввод п-количество злементов больше нуля
cin >> п; // побольше нуля и меньше количества участков памяти
for {i=0; i < п; itt)
cin >> ali]; // ввод элементов массивов доступ по индексу
for {i=0; i < п; itt}
b[i]=xe [i]; // 4TeHMe MS массива констант x
cout << “ массив “<< endl;
// вывод массива с использование индекса
for (i=0; i « п: itt) cout << bli] <0"
pee
cout << епа1;;
Базовые залачи обработки массивов. Базовые задачи обработки массивов
включают в себя поиск срелнего значения и поиск индексов минимального и
максимального элементов. Программная реализация указанных залач рассматри-
вается в контексте описаний предыдущего примера как фрагменты программ.
2) Статические массизы. Cramuveckoe связывание
29
Вычисление средних значений по заданному критерию. Из решения этой
задачи вытекает решение прутих базовых задач: поиск вхожлений образца,
формирование пругого массива по заланному критерию и удаление заданных.
Например. вычисляют среднее нечетных элементов и формируют новый
массив из заданных элементов (назначение индексов: н — количество эле-
ментов: { — залание индекса элемента исхопного массива: / — индекс нового
и счетчик количества):
For {i-0, 1=0, s-O; i «п; itt)
i (a[i]% 2)
{в+=а[1];
c[j++]=a[il;}
if(j>0)¢
s/=47
cout << “ массив с “<< endl;
for{i-0;i=jЕitt)
cout << c[i] << *
mites
Поиск экстремумов в массиве. Положение элемента в массиве опрелеля-
ется его индексом, поэтому решение задачи сволится к опрелелению индекса
элемента. Если целая переменная imax (imin) соответствует индексу мак-
симального (минимального) элемента, то максимальный элемент — a [imax]
(mam alimin])}.
Алгоритм поиска экстремума в части массива начиная с запанного ин-
декса k будет слелующим: задаются начальные значения инпекса imax=k;
в цикле просматриваются элементы массива. Если текуший а[2] > alimax].
TO индекс imax становится равным текущему: зпак:=: (аналогично для по-
иска минимального); поиск во всем массиве: К=0.
Фратмент программной реализации задачи, использующий доступ по
индексу:
for {k=0, i=k, imax=-0; i < п; itt]
if (а[1] > al[imax]}
imax=i;
max—a [imax];
Обмен значениями элементов массива. Лля перестановки элементов при-
меняется вспомогательная переменная but, выполняющая роль буфера, тип
которой определяется типом компонент Обмен значениями первого макси-
мального с элементом с нулевым инпексом:
=
БаЕ=а[0];
a[0]=a[limax];
a[limax]=buf;
Вложенные циклы. Практически все залачи обработки массивов ис-
пользуют в теле цикла другие циклы. В этом случае телом цикла является
прутой цикл. Тела ЦИЕЛОЕ He пересекаются, т. е. на кажпом шаге внешнего
выполняется вложенный цикл. В качестве примера рассматривается алго-
ритм сортировки массива метолом полного перебора, в котором использу-
ются рассмотренные выше ползадачи: алгоритм определения максимального
(минимального) элеменга массива и его индекса в части массива начиная с
произвольного значения номера элемента / и до конца и обмен значений. Най-
денный элеменгс индексом imax обменивается местами с эзлементом— a[j],c
30
Глава 2. Способы представления структур данных. Составные тины
которого начинается просмотр. На/-м шаге часть массива от начала массива
до индекса / оказывается отсортированной, а оставшаяся неотсортированной.
Алгоритм реализуется двумя циклами: во внешнем цикле запаются граница
межлу отсортированной и неотсортированной частями и обмен значений,
BO вложенном пикле выполняется поиск позиции первого экстремума {мак-
симума или минимума) Е неотсортированной части. Задачи поиска индекса
в части массива и обмен значениями рассмотрены выше. Для решения He-
обходимо ортанизовать внешний цикл, залающий просматриваемую часть
массива Фратмент программной реализации залачи'
Os Semaspe
поиск максимума начиная с злемента с номером 7
For {i=j, imax-j; i < ny itt)
if (e[i] > climax]} imax=i;
обмен злемент B массива
buf=c[j]; ©[5]=С[
о:1.Гг=Я-
climax]; c[imax]=buf;
2.2. Статические многомерные массивы. Матрицы
Массивы с более чем одной размерностью называются многомернымы.
Число инлексов. которые необходимо указать лля получения поступа к от-
дельному элементу массива. определяется его размерностью. Максимально
возможное число размерностей определяется компилятором. Многомерные
массивы в языке C++ конструируются из одномерных. Лля них можно при-
менить рекурсивное определение «массив массивов» для кажлого измерения.
Размер памяти растет зеспоненциально с увеличением числа измерений.
Статические многомерные массивы задаются указанием кажлого изме-
рения в квапратных скобках. Лля объявления необходимо указать тип эле-
ментов. имя и количество элементов по кажлому измерению. Объявление
многомерного массива. память под который выделяется на этапе компиляции:
<тип> ‹хилентификатор>[<количество 1>]..[«количество i*]..
[«количество m>];
При обращении к элементам вычисляется каждый инпекс. Многомерные
массивы размешаются таким образом, чтобы при переходе к следующему
элементу быстрее всего изменился бы последний индекс. В памяти многомер-
ный массив располагается в последовательных ячейках построчно. Базовый
практический случай — лвумерный массив. являющийся аналогом матрицы
или таблицы. Объявление статического двумерного массива:
«Тип злемента> «идентификатор>[«количество строкЪ] [<ке-
личество столбцов];
В простейшем случае тип компоненты является базовым типом (число-
вым. символьным, логическим). Объявление константы для задания макси-
мально выделяемой памяти:
22 Статические мнозомерные массияы. Менрицы
31
const int птах=100;
float b[nmax][nmax]; //Объявление двумерного массива
Объявление двумерного массива из 100 строк и 100 столбцов:
int а[100] [109];
Для лоступа к отдельной компоненте надо знать номер строки /, по кото-
рому можно определить начало участка под ланную строку и номер элемента
в этой строке. Каждый их этих индексов может изменяться от 0 до заданного
количества. Доступ к элементу в /-й строке и /-м столбие:
«идентификатор >[<номер строкиЪ] [номер столбиа>];
Для обработки матриц необходимо использовать два цикла: один задает
номер строки, другой — номер столбиа. В общем случае решение матричных
задач осуществляется не менее чем двумя циклами. Задание значений эле-
ментов матрицы может быть выполнено способами, аналогичными заданию
массива. Например. элементам присваивается значение счетчика А:
for fint 1=0, int k=O; i < п; itt)
for{intj-O0;9<м9+)
afi] [j]=k++;
Вывод матриц по строкам:
for fint 1=0;: i < пр itt){
Fer {int 3-0; 93 < mF 7+7)
cout << ee: <a
a
cout << endl;
Для создания и обработки статических матриц формируется программ-
ный шаблон. в котором реализованы алгоритмами разные способы залания
элементов. При инициализации многомерного массива констант он либо
представляется как массив из массивов, при этом каждый массив заключает-
ся в свои фигурные скобки (в этом случае левую размерность при описании
можно не указывать), либо задается общий список элементов в том порядке,
в котором элементы располагаются в памяти:
#include <icstream>
using namespace std;
const int nmax=L50;
void main{) 1
// описание матриц, память выделяется на этапе компиляции
int at[nmax][nmax], ct [nmax] [пак];
int ind[nmax], b[nmmax]; float vw[nmax];
float a[mmax] [nmax];
// задание матрицы констант для тестов
// инициализация матрицы констант по строкам
double БЕ[] [5)={{4,7,9,3,12}, {13,25,7,6,9}, {3,7,4,4,16},
1,5,8,9,3}, 124,6,8,6,18}};
if инициализация массива констант
32
Глава 2 Способы представления структур данных. Состааные мины
int. УЕ
Та, 2a То, Зет, Ворот,
// вспомогательные переменные
int 1, Е, m, j, i, 31, imax, пах, max, buf, obr;
float а, bufi;
// ввод числа строк столбцов для отладки п=2; ПЕР
cin >> nh мп;
for { 1=0; i<ny itt+ }
for{j=0;<: qs
cin>> at{il[j]; // ввод элементов
// чтение из массива констант
for { 1=0 ,„1=0: 140; itt }
For{1-9jam++
et [11 [j]=ytll++];
// чтение из матрицы констант
for { 1=0; i<m; itt }
Eor { 3-07 рем; +)
ala] [j]=bt [2] 151;
cout << «\ nmatr а \п” ; // вывод по строкам матрицы
for{i=0;10:it+}{
For | 4-07: еше)
cout << afi][j] << *”
ae
cout<<end1;
}
}
Базовые задачи обработки матриц. Базовые запачи обработки матриц
включают в себя поиск среднего значения. поиск индексов минимального
и максимального элементов. В алгоритмах обработки матрии можно выле-
лить два класса: обработка всей матрицы, обработка матрицы по строкам
или столбцам. Программная реализация указанных задач рассматривается в
контексте описаний прелылушего примера как фрагменты программ. Опре-
пеление индекса максимального элемента в строках матрицы:
for{1=0:ап:itt11
for { j=0, Jmax—-O; jem; ++ )
if { a[i]l[j]> a[i] [jmax])}
jmMax=j;
b[ij=jmax;
Использование косвенной апресации: HHOeKC строки или столбца опре-
деляется через другой массив b[i]: а[1] [b[[i]].
Решение предыпущей задачи без промежуточной переменной:
For | 1502 isn; itt )
for { 4=0, b[i]=0; jem; 44+ )
Е(РГ=СНBal7}
b[i]=jmax;
Вычисление массива из сумм элементов кажлой строки:
27 Лннамические переменные
33
For (1=0: i < nz itt)
for {j=0, v[iJ=O; j =< my; 4+)
м [1] +=а [1] [1];
Поиск экстремумов на диагоналях квадратной матрицы. Элементы на диз-
гоналях квапратной матрицы и = м определяются значением одного индекса:
строки или столбца, второй выражается через первый. Наглавной диагонали
индексы строки и столбца одинаковы. сам элемент — ali] [i], на побочной
индекс столбца / выражается через индекс строки /: 1=1-1-1; сам элеменг. —
ali] [n-1-i]. Диагонали квапратных матрид обрабатываются одним циклом.
Поиск индекса минимального элемента на побочной диагонали:
for (1=0, imin=0; i < п ; itt)
if (afli][n-1-i] < a[imin] [n-1-imin])
imin-i;
cout << “ \n min= ” << aflimin][n-l1-imin] << endl;
Поиск максимального индекса Ha главной диагонали:
For {i=0, imax=-0; i < п; i++}
if ( а[1][1] > al[imax] [imax] }
imax=i;
cout << “\n max= \п ” << alimax] [imax] << endl;
2.3. Динамические переменные
Переменные. которые создаются по требованию и запоминаются в
линамической области памяти переменного размера (куче), называются
Эынамическими. Динамические переменные называются также переменными,
апресуемьми указателями. Линамические переменные не определяются на
этапе компиляции, так как память поп них выделяется на этапе выполнения.
В связи с этим у них нет имени. Они определяются адресами в памяти. Об-
рашение к динамическим переменным осуществляется не по имени, а по их
апресу в памяти (указателю).
Основное применение указателей — выпеление неименованных областей
в куче и единственный способ доступа к этой памяти указатели. Указашель —
это переменная. содержашая апрес области памяти другой переменной. Ука-
затель представляет собой произволный тип. Он связывается с некоторым
типом данных. значением указателя является адрес ланных опрепеленного
типа — основного или производного (типизированный указатель). Использу-
ются три типа указателей: на переменную заданного типа, фунЕЦИЮ, тип void.
В качестве типа при определении указателя может быть использован Kak
OCHOBHOH ТИП, ТАЕ И ПроизвОЛНЫЙ. Основные типы данных определяются
ключевыми словами:
char, int, float, long, double, short, unsiqned, signed, void.
Для объявления указателей используется специальный символ «*». перед
34
Глава 2. Способы npedcmagienua скнруюкнур данных. Составные тыны
которым указывается тип данных. Объявление типизированного указателя на
объект определенного типа:
«Вазовый тип указателя MIM тип адресуемой области памяти» *%
«идентификатор переменной указательЪ;
Или коротЕо' «тип» “ хуказательъ!
THe «базовый тып указателя» определяется типом переменной, Ha KOTO-
рую он может ссылаться. Тип может быть любым, кроме ссылки и битового
поля. Указатель Ha void может адресовать область любого типа. но при вы-
делении памяти он должен быть преобразован к опрелеленному типу.
Переменная-указатель содержит адрес области памяти, размер которой
определяется базовым типом указателя. Поскольку память пол динамические
переменные кылеляется на этапе выполнения программы. при залании типа
указателя допускается применение типа, который еше не определен. но он
должен быть к этому моменту объявлен. Примеры описаний;
int: ЧРЕЗ “pes: float “pt3, *pt4; double ‘pts;
Указатель — это апрес области памяти под некоторую переменную. Имя
указателя — местоположение. Отличие типизированных указателей от ма-
шинных апресов состоит в том, что указатель может содержать апреса ланных
только определенного типа.
Для захвата области в куче для размешения переменной. на которую
указывает указатель (int, float, doble ит п.), используется операция new:
<указатель >=new «базовый тып указателяз;
В этом объявлении создается переменная-указатель на <тип компо-
нент>». В результате операции new в куче выделяется непрерывная область
байтов лля размещения переменной типа <базсвый тип указателя», а
указателю присваивается адрес этой области:
РЕ1-—пем 1106: pt3— new float ; ptS- new doble; int k,n;
Эта память не имеет имени в отличие от обычных переменных. Един-
ственный способ доступа к этой памяти — указатели. По умолчанию после
создания динамическая переменная содержит неопределенное значение.
Доступ по указателю. Операция разадресации (разыменования). Лля досту-
па к выделенной области памяти применяется операция разалресации или
разыменования указателя, ч*»:
“имя переменной типа указательъь;
Эта конструкция является динамической переменной, к которой приме-
няются BCE операции. попустимые для переменных базового типа. а запись
*<указатель» можно рассматривать Kak ее имя, аналогично обычной пе-
ременной. операции с которой выполняются по имени. Динамическая пере-
менная имеет тип, совпадающий с базовым типом указателя. Указатель при
обрашении к памяти «работает» с участком смежных байтов. количество ко-
торых определяется функцией sizeot |}. Операция *<указатель» зависит
23. финачические переменные
35
не только от значения указателя, но и от его типа. При поступе
к памяти с
помолтью разыменования указателя требуется информация не только о раз-
мешении. но и о размерах участка памяти, который булет использоваться.
Указатель char “ptr при обрашении к памяти работает с участком в | байт
указатель float “ptr — с 8 смежными
байтами памяти и т д. в общем слу-
чае указатель <тип> “ptr работает с участком, количество байтов которого
определяется функцией sizeot(< тип»).
Кроме операции разадресации нал указателями допускаются операции
присваивания и отношения. Операция присваивания попускается только
между указателями
одного и тогоже типа. Указателю можно присвоить зна-
чение пругого указателя
или нулевой ampec. Равенство указателей понимается
Eak совпадение адресов. т.е. назначение их на одну и ту же область памяти
(переменную). Указателю можно присвоить нулевое значение или nui, где
ний— константа. определенная в языке как указатель равный нулю: pir — 0.
Это значение адреса называется пий-указателем или пустым указателем, ко-
торый ни на что не ссылается. Пустой указатель не может быть использован
в правильно работающей программе для размешения данных.
Динамическая переменная *<указатель> применяется в выражениях
как обычная переменная. Конструкция *<указатель> может использоваться
слева и справа в операторе присваивания. Если операция +«*”» применяется
справа, то понимается каЕ «получение значения, размешенного по адресу.
равному значению указателя»: если операция «*» используется слева от знака
операции присваивания или в операторе ввола данных, то понимается как
«размешение значения по адресу равному значению указателя».
В выражениях
*«указатель>=<«высажение базового типа указателя»;
*<указатель_1>=*<указатель_2 >;
осушествляется размешение значений по указателю — запись значений в
выделенную область памяти:
*ptl=5; — значению динамической переменной присваивается зна-
чение обычной переменой:
*pt2=“ptl;“pt2=“ptl+3; “ptl=“ptl +“pt2; — модификация
значений динамических переменных.
В выражении
<переменная базового типа>=“‹указатель>;
значение динамической переменной записывается в область памяти обычной
переменной: k=“ptl;
Операции взятия адреса. Указатель также может получить значение посред-
ством операции взятия адреса статической или динамической переменной:
& < переменная базового типа указателя»;
Например, лля предылущего описания:
ptel=eEk; РЕЗЕЕ (*ptl};
Значение апреса присваивается указателю соответствующего типа.
36
Гоава 2. Способы предотааленыя структур данных Составные mun
Операция *<указатель> оказывается обратной к операпии взятия адреса
&: k= “ck:
Приоритет унарных операций разапресации и взятия алреса выше. чем
арифметических. кроме унарного минуса Поэтому в выражениях вида
*<имя переменной типа указатель>—
*<имя переменысй типа указатель> <армёметинеская операния>» К;
где пол <сперация> понимается любая арифметичесевя операция: k — пере-
менная соответствующего типа. сначала выполняется операция разапресации
указателя, так как унарная операция «<*> имеет более высокий приоритет. чем
арифметические операции: переменная молифицируется в соответствии со
знаком операпии (молифипируется значение линамической переменной:
прибавляет A, умножает Ha k i T. 1).
Краткая запись модификации переменной в прелыдушем выражении:
еременно и типа указатель» «о пер апия>=Е Е
Динамическую
память можно не только забирать
из кучи. но и возврашать
в кучу Для освобождения памяти используется операция delete:
gelete «указатель >;
Олним из способов инициализации указателя является операция взятия
адреса & обычной переменной Пример инициапизации указателя алресом
обычной переменной:
void main(){
int п, E,m, “pt, “*ptl; E=-9; pt=Ek;&К;
// получение значения, размещенного по адресу
*ptl=7;
// размещение значения, по адресу
Создание линамических переменных с использованием операции выде-
ления памяти и размешение значений по указателям:
void маза (){
int *pa, *pb; ра=0;
/! вышеление памяти и инициализация динамической переменной
pbh=new int ; *#nb=9 ;
pa=new int; // выделение памяти
// значение “pb записывается 5 область “pa
*pa=“pb; // “pa присваивается значение переменной *pb
В заключение выделим основные положения лля применения указателей:
1} указатель — это переменная. подобная обычным переменным:
2) переменная типа указатель содержит адрес области памяти, размер
которой определяется базовым типом указателя:
=>
24 Adpecnaa apuduwemuxa, Указатель и дниамические массивы
37
3) указатель ссылается Ha динамическую переменную, апресуя область,
выделенную пол переменную:
4) указатель можно инициализировать, используя операцию new, опера-
цию взятия адреса переменной &:
5) к указателю применяется операция разапресации (разыменования),
которая позволяет получить доступ к области памяти динамической пере-
мМенной -‹указатель».
2.4. Адресная арифметика. Указатели и динамические массивы
Динамические массивы создаются в динамической области памяти на
этапе выполнения программы. В отличие от статических массивов размер
динамического массива можно задавать во время выполнения программы.
Преимущества динамических массивов состоят в том, что размерность может
быть переменной и объем памяти, выделяемой под массив, определяется
на этапе выполнения программы и освобождается по требованию. Для ди-
намического выделения памяти массиву применяются указатели, базовый
тип которых определяется типом компонент массива. Эта неименованная
область памяти находится в куче (свободной динамической области памяти,
расположенной межпу сегментами кола программы, сегментами данных и
стеком). Рассмотрим применение нотации указателей при создании и обра-
ботке динамических массивов.
Объявление указателя на динамический массив:
<тып компоненты массива» * <идентификатор указателя? ;
В этом объявлении создается переменная-указатель на <тип комоонен-
ты». Лля динамического выпеления памяти пол массив используется оператор
New, при этом необхолимо указать тип компоненты и количество компонент
Создание динамического массива и выделение памяти в куче:
<указатель-идентификатор массива>—
пем «тип компоненты >[<количество компонент>];
или
«переменная указатель» =
new «Вазовыйм тып указателя» [«количество>];
Начальные значения для элементов массива при этом не задаются. Вы-
деление памяти в куче осушествляется пол два массива действительных и
пелых чисел: указатели получают апрес начала своей области:
int п;
int*pt;
float “р;
cin>>ny;pt=new int[n]; p= new float [n];
B результате операции new в куче выделяется непрерывная область пля
размешения заданного количества компонент: заданное количество участков
38
Глава 2. Способы npedemaaienua структур данных. Состааные mines
памяти соответствует базовому типу указателя: апрес начала присваивается
указателю, что можно трактовать как выделение памяти под динамический
массив. Память поп указатель выделяется на этапе компиляции. Размер BEI-
деленной области определяется через фунЕЦИЮ sizeof {<тип»):
Sizeoct ([«Вазовый тип указателя»! * «количество»;
Динамическую память можно не только забирать из кучи, но и возЕра-
шать в нее. Освобождение памяти. выделенной с помошью операции new,
выполняется с помошью оператора delete, размерность не указывается:
delete []<«идентификатор указателя»;
delete []pb; delete []pa;
Переменная-указатель может трактоваться как имя массива. Через имя
можно обрашаться к любому элементу массива. Все обрашения в програм-
ме к переменной по ее имени заменяются компилятором на адрес области
памяти. в которой хранится значение переменной. Лоступ непосредственно
по указателю эффекгивнее. Доступ к выделенной памяти в элементам дина-
мического массива осуществляется по указателю в соответствии с правилами
апресной арифметики.
Арифметические операции над указателями. Адресная арифметика приме-
няется только для типизированных указателей, апресуюущих динамические
струкТУры данных, последовательно размешенные в памяти. К указателям
применяются только де арифметические операции: сложение и вычитание.
Алпресная арифметика включает в себя слепующие операции:
* инкремент (++):
* декремент (——):
* сложение и вычитание указателя с константой:
* вычитание указателей.
Рассмотрим операции инкремента и декремента указателя.
Инкремент указателей: унарная операция «++» — операция единичного
прирашения, как и прибавление епиничной константы, изменяет значение
указателя на величину sizeof («тип»), где <тип»*“ — тип указателя: ука-
затёель «перемешается» к соседнему объекту с большим апресом.
Декремент указателей: унарная операция «—» — операция елиничного
вычитания. изменяет конкретное численное значение указателя на величину
в1=е0Е (type), Ie <type>* — тип указателя: указатель «перемешаетсяь к
соседнему объекту с меньшим адресом. В зависимости от формы (постфикс-
ная или префиксная. до операнда-указателя или после него) выполнение
унарных операций «++» и «—» осушествляется либо до использования. либо
после использования значения указателя.
Смысл операции «побавление 1 к указателю» состоит в том. что прира-
шение масштабируется размером памяги, занимаемой объектом.
Операция сложения указателя с целочисленным значением Г при этом
числовое значение указателя возрастает на величину j~sizectl(<type>}),
указатель перемещается на / объектов в сторону роста адресов.
24. Ларесная арифметика. Указатели и Оннамические массивы
39
Операция вычитания указателя с пелочисленным значением/ при этом
числовое значение указателя уменьшается на величину j*sizeof(type),
указатель перемешается Ha / объектов в сторону уменьшения адресов.
Указатели нельзя складывать, умножать, делить, но к ним применимо
вычитание как к объектам одного типа. Вычитая два указателя одного типа.
можно определять количество объектов базового типа. расположенных межлу
пвумя указателями {чрасстояние» межлу пвумя участками памяти). «Расстоя-
ние» определяется в ецдиницах. кратных Due (в байтах) объекта Toro типа. Е
которому отнесен указатель. Разность указателей, адресующих два смежных
объекта люФого типа. по абсолютному значению всегла равна единице.
Ассоциативность операций разалресации, инкремента и декремента. Распо-
ложенные рядом унарные операции имеют одинаковый приоригет и выпол-
няются справа налево. Если pt — указатель, то *рЕ++ — типовое выражение
{илиома — устойчивая конструкция, применяемое как единое целое) исполь-
зуется при низкоуровневой работе с указателями в циклах. Ее назначение —
получить значение по указателю и увеличить его. Аналогично содержание
*рЕ — получить значение по указателю и уменьшить значение указателя.
Операторы +*» и «++» имеют одинаковый приоритет. поэтому порядок
выполнения определяется ассоцпиативностью. В записи *pt++ скобки неявно
расставлены следующим образом: * (p++). Сначала выполняется постфикс-
ная операция «++», но. так как инкремент постфиксный. он выполняется
после разадресации, т.е. сначала используется значение указателя и по нему
получается значение переменной. которое служит значением выражения
в целом, затем увеличиваем значение указателя: выполняется операция «+++,
и значение указателя увеличивается на |:
“ptt+ — в записи совмещаются два действия: возврашение значения
текушего элемента массива и переход к следующему элементу:
“TTD — в этом выражении сначала выполняется префиксная операция
ett, а затем разадресация, т е. сначала увеличивается значение указателя
на единицу памяти, потом по этому значению указателя получаем значение
переменной:
(*pt)++ — в этом выражении увеличивается переменная, на которую
указывает px. Сначала выполняется операция в круглых скобках: осущест-
вляется разапресация, затем увеличивается значение выражения:
-+-55 — в этом выражении указатель разалресовывается и увеличива-
ется значение переменной по указателю;
--"pt — В этом выражении уменьшается значение переменной по
указателю
++*++pt — вэтом выражении увеличивается и указатель, и значение по
указателю: сначала возрастает значение указателя. затем по этому указателю
отыскивается значение, которое потом увеличивается.
Иллюстрация приоритетов операций:
Foid ша1п()1
int2“вNoЕ:
р-=ёК;
j="ak; // значение Е
++Е; // Е, увеличенное на
40
Глава 2. Способы представления структур данных. Составные мины
j="p; // 1] присваивается значение k
*р++; // значение k, р увеличивается на 1
*--6; // значение К, сначала уменьшается op на 1
++*р; // Е, увеличенное на 1
При сравнении указателей с использованием операций «больше» или
«меньше» выполняется сравнение соответствующих адресов Kak беззнаковых
переменных. Если указатели ссылаются на одну и Ty же область памяти (мас-
CHE), то это сравнение понимается как «ближе или дальше от начала массива».
Косвенная адресация. Базовый тип указателя сам может быть указательным
типом. т.е. объявляется указатель на указатель. Для объявления в программе
указателя, который будет хранить апрес другого указателя. следует удвоить
число звездочек в объявлении:
<тип» ** «указатель»;
Количество указателей в пепочке. запающее уровень косвенной адреса-
UHH, соответствует числу звездочек перед именем идентификатора Уровень
косвенной апресации определяет. сколько раз следует выполнить операцию
раскрытия указателя. чтобы получить значение конечной переменной.
Косвенная айресация — обрашение к области памяти не напрямую по
апресу а через объект. которому в памяти соответствует определенный учас-
ток. Код с косвенной апресацией. в котором создаются два указателя с раз-
личными уровнями косвенной апресации:
int 1]:
inte "py ОЕ М ОРЕХ
=-2
Еее
Жеа
-==г a
7—3; prays
в;
PE-e&p;
ИЖЕ
Г значение 1 равно 8
coute< “pt; // значение 8
Ассоциативность унарной операции разыменования — справа налево, по-
этому последовательно обеспечиваются доступ к участку памяти с адресом г.
Последовательность разыменований поясняется выражением с использова-
нием скобок: (* (*ot)). Разыменование указателя “pt соответствует типу
указателя р, еше одно разыменование **pt обеспечивает доступ к /.
Доступ к элементам динамических массивов. Доступ к элементам динами-
ческих массивов реализуется по указанным правилам адресной арифметики.
Если ot является указателем, то каким бы ни был тип объекта, на KOTO-
рый он указывает, операция ptt++ увеличивает pt так, что он указывает на
следующий элемент при последовательном размешении объектов. а опера-
ция pt+ i увеличивает pt так, что он указывает на элемент, отстоящий на
г элементов от текущего элемента.
Онерацыя 2=-- уменышаем ot так, что он указывает на предыдущий эле-
мент при последовательном размещении объектов, а операция р=+: умень-
шает ot так, что он указывает на элемент. отстоящий назад на / элементов
от текушего элемента.
Вычитание указателей: если pt и ptl указывают на элементы одного и
TOTO же блока (массива). To pt-pt l — количество элементов межлу ними.
24. Ларесная арифметика. Указамели и дниамические массивы
41
Нал указателями выполняют слепующие операции:
1) разыменование или доступ по апресу (*):
2) присваивание;
3) получение (взятие) апреса (&):
4+) инкремент, декремент:
5) адлигивные операции (сложение и вычитание с константой);
6) операции отношений (==, !=, >, <). Для указателей, ааресующих
линамические структуры данных, попускается использовать операции отно-
шения! сравнения значений адресов на равенство и неравенство и операции
«больше» И «меньше».
Переменная-указатель трактуется как имя массива. Через имя можно
обрашаться к любому элементу массива. Все обращения в программе вк пе-
ременной по ее имени заменяются компилятором Ha апрес области памяти.
в которой хранится значение переменной. На основе принпипов апресной
арифметики для лоступа к i-My элементу необходимо вычислить апрес -И
области и полученный указатель разадресовать. При выделении памяти в
куче указатель получает адрес начала этой области. Увеличение указателя на
Гепиниц памяти устанавливает ето на начало области под !-Йй элемент Если
ot указатель. по которому выделяется память, то pt+i — адрес Г-Й области.
Лля описанных выше массивов
ptti- адрес, “{рб+1) - значение по этому адресу
Любое выражение, вкЛЮЧаАнушее в себя массивы и инпеёксы, может быть
записано через указатели и смешения. Можно применять доступ по индексу
или поступ по указателю через разыменование указателя:
«указатель» [1] соответствует
* [< [указатель+т)
* {рЕ+: } соотБетствует pt[il].
Запись pt [i] соответствует элементу массива через / позиций от нача-
ла: pt [i] преобразуется K виду * (2+1). где pt+i — апрес -го элемента
OT начала.
Для того чтобы не потерять значение указателя на начало области. алрес-
ная арифметика может быть использована с другим указателем. который
связывается с массивом. Этот вспомотательный указатель можно установить
на начало области: pa=pt, после чето Е нему применяется Kak операция
инкременга. так и операция декремента: ра++, ра--. т е. он может переме-
шаться по элементам динамического массива. Вспомогательный указатель
играет роль текушего указателя.
Если ра указывает на некоторый элемент массива, то ра++ — на следующий
элемент ра-1 — на элемент. стоящий на Г позиций до элемента, указываемого ра,
pati — на элемент, стоящий на / позиций после элемента. Если pati — agpec i-ro
элемента, то * (pati) — содержимое этого участка, соответствующего /-му элементу
Имя статического массыва — EOHCTaHTa, препставляющая собой указатель
на нулевой элемент массива. Этот указатель отличается от обычных указателей
тем, что его нельзя изменить (установить на другую переменную) „Так Kak OH
не хранится в переменной, а является постоянным адресом. Адресная ариф-
метика может использоваться с дпругим указателем, которому присваивается
4?
Глаза 2. Способы представления структур данных. Соскаяные зпыны
адрес первого элемента: ра=а, после чего к нему может применяться опера-
пия как инкремента. так и лекремента.
Текуший указатель может перемешаться по элементам как динамиче-
ских. так и статических массивов. Если ра — указатель. то присваивание
р-ва [0] приводит Е тому, что ра указывает на нулевой элемент массива! pa
содержит апрес элемента а[0]. Присваивание са=ёа[0] можно записать
Kak ра-8.
Для описанных выше указателей массивов возможны слелующие вари-
анты доступа к элеменгу с индексом #
ра[1] или *“(pati), или “fp,
где в — текуший указатель, который вычисляется в ЦИЕЛе.
Залание значений элементов массива может быть выполнено BROTOM с
клавиатуры, чтением из массива констант, моделированием. Ниже рассма-
тривается базовая заготовка программы Kak шаблон OIG созлания динами-
Ческих массивов.
Объявляются указатели: int *а, *b, *с,*репЯ,*ре, *в;
В результате выполнения операции new они получают апреса непрерыв-
ных областей памяти в куче:
а=пем int[n]; b = new int [0]; с = new int [n];
Способы обработки динамических массивов иллюстрируются на примере
ввода элементов массивов. Доступ к элементу по индексу:
for {1=0;: 1 < п: itt) cin >> ali];
Доступ по указателю через смешение от начала:
for(1=0:i<п:itt)cin>>“+):
Доступ по текущему указателю. который смещается на каждом шаге цикла
на следующий элемент. pend=c+n! — апрес конца динамического массива‘
for (р-с; в < pend; pt+) cin >> “p;
Чтение из массива b в массив с:
for {Р-=Б, pt=c; p <btn; ptt) “БЕ;
B заключение теоретической части привепем принципиальные положения
описания и обработки массивов:
1) олномерный массив — объединение М компонентолпного типа, записан-
ных в последовательных ячейках памяти размером в1=есЁ(<тип»|! байтов,
те. в массиве хранятся отдельные значения одного типа — элементы массива,
которые сохраняются в памяти последовательно:
2) память под массивы выделяется по принципам динамического или
статического связывания:
3) имя области памяти в куче — имя массива, совпалающее с именем
указателя‘
4) поступ к отдельному элементу опрепеляется по его индексу (номеру)
или по текушему апресу первый элемент имеет нулевое смешение апреса,
те нулевой индекс, имя статического массива является константой и содер-
жит апрес первого элемента.
24. Ларесная арифметика. Указанелы и Ониамические массивы
43
Алгоритмы и программы обработки динамических массивов. Базовыми
для моделирования задач обработки структур данных являются следующие
алгоритмы:
* вычисление срепних значений по заданным критериям:
* поиск элементов по заланным критериям;
* вставка, удапение запанных элементов;
* вычислительные запачи:
* сортировка;
* формирование массивов по заланным критериям:
* объединение и пересечение:
* вычислительные запачи преобразования и формирования массивов.
Программная реализация указанных залач рассматривается в прелполо-
жении описаний переменных, приведенных в базовом шаблоне:
void main{){
int xweljJH{bl, 2, 53; 4, 35, 16; 7,65}
int 1,:K,.m,°j,; 0; 1, БЕ, ns, nr, wax, imax;
// описание целых переменных
int *а, *b, *c, *p, *pend, “pa, “pb, “pmax; // указатели на int
float *г, “rd; // указатели на float
float sum, 3, 31, bufl; // описание действительных переменных
cin >> п; // выделение памяти под пинамические массивы
/;5Б а, Bb, с адреса начала в переменных указателях
а=пем int[n]; b=new int[n];
‘f задание элементов массивов массивом констант
For {i=0; i < п; itt) а[1]= [1];
fof
ввод элементов массивов, доступ по индексу
for {1=0;: 1 < mp itt) cin >> bli];
'’ вывод массива с использование индекса:
cout << endl<e<” MmassivTM <<endl;
for { 1=0;
1<mritt)cout<<bli]<<”
he
Поиск элемента no образцу в массиве. Решение запачи проверки вхож-
дения элемента в массив имеет слепующие варианты: нет вхожлений, одно
вхождение, несколько вхожлений. В двух послепних случаях требуется найти
индексы вхожлений. т.е. инлексы элементов, с которыми совпадает образен.
В случае нескольких вхождений рассматриваются задачи опрепеления индек-
са первого вхождения. последнего или массива индексов всех вхождений.
Решение может быть получено с применением структурных и неструЕ-
турных алгоритмов. Структурный алгоритм использует цикл while и целую
переменную / флага перестановки. которая рассматривается как логический
признак определения вхождения. Пока не конец массива и флаг равен 0,
выполняется цикл while. Если элемент и образец совпали, то флаг сбра-
сывается и осуществляется выход из пикла; если совпадений нет, то выход
осуществляется по достижении границы массива.
AA
Глава 2 Croenin представления cmpycmyp данных. Cocrnaanate Mune
Код фратмента программы:
int index=-0;
1=0; £=1L;
while(i<n&&£)
if (a[i] == obr) {
Е=0; // элемент найден
index=i;
}
else itt;
Ha выходе анализируется значение флага, и по значению индекса опре-
деляется первое вхождение.
Структурный алгоритм поиска первого вхождения можно реализовать
ЦИЕЛОМ while, который выполняется. пока истинно условие: текуший эле-
мент не равен образцу и не достигнут конец массива:
&& (а[7] != obr})
7++; // идем по эзлементаы, не равным образцу
Выход из цикла осуществляется в лБУух случаях: совпаление или дости-
жение конца массива. Для решения анализируется значение элемента, при
выполнении условия осушествляется BROOD из ЦИЕЛа:
ay
if (a[j] == ebr) cout << “” ивдекс вхождения
else cout << “” не входит * << endl;
<< 7 << endl;
Поиск экстремумов в массиве среди заданных элементов. В общем случае
поиска экстремумов среди элементов. запанных некоторыми условиями, ал-
горитм подразделяется на две части: выделение первого вхождения запанного
элемента и определение начального значения апреса (индекса) для поиска.
Далее начиная с позиции вхождения в пикле просматриваются элементы
массива, осуществляются выбор заданных элементов массива и сравнение с
текушим значением экстремума.
Решение запачи сводится к определению позиции элемента. Положение
элемента в массиве зависит от инпекса или адреса. Если целая переменная
imax (1110) соответствует индексу максимального (минимального) эле-
мента. то максимальный элемент — a [imax], минимальный — a[imin]. Ina
решения в условном операторе вволятся условия критерия выбора элементов.
Рассмотрим алгоритм решения задачи поиска максимального срели эле-
ментов, кратных трем или пяти, использующий поступ по индексу: сначала
определяется первое вхождение (если входит, то эта позиция является началь-
ной для поиска). Фрагмент программы поиска первого вхождения элемента
по заданному критерик
1=0;
while(i<п5&{ali]%3&Еа[1]%5})
1++; // вывВираются элементы, не равные образцу
// проверка заданного критерия - кратные 3 mam 5
if ({!fa[i] %3) | 1(а[2] &%5)) {
поиск максимального, начиная с индекса вхождения i
for {]=1, imax-i; ] < mn; 9+)
24 Ларесная apuguemuxa. Указанели и ониамицеские массияы
45
if выбор элементов по заданным критериям
и сравненые с текущим максимальным
if {а[7] > alimax] «ce {!(а[7] * 3) | 1891 % 5})}
imax—j;
}
else cout << “
if
нет вхождений “” ;
Формирование из одного массива другого по заданным критериям. При фор-
мировании массива из элементов другого массива, удовлетворяющих опреде-
ленным условиям или преобразованных по какому-либо закону, необходимо
учитывать правила изменения индексов элементов исхолных и формируемых
массивов. При решении вволятся разные переменные для инлексации эле-
ментов массивов. Например, четные элементы массива переписываются в
пругой массив. Salata подразделяется на пве подзапачи. Сначала вычисляется
количество заланных элементов и выпеляется память под новый массив. затем
формируется массив из заланных элементов. При решении первой ползалачи
в качестве счетчика заданных элементов используется переменная /
for {1—0 бел ео att}
i=('{(а[1]%2))
++; „р выбор четных
На выхопе цикла: / — количество четных. По этому значению / выделя-
ется область памяти под новый массив:
с=пем int [7]:
Формирование нового массива выполняется в следующем фрагменте:
For {i=0, j=0; 1 < ny itt)
if (! ( а[1] & 2)) c[ 1++] =alil; // выбор четных
Слияние двух упорядоченных массивов в один новый массив. Новый массив
полжен включать в себя все элементы лвух исходных массивов таким обра-
зом, чтобы они оказались упорялоченными. Задача по формированию из
нескольких массивов опного по заданному критерию является обратной Е
прелылущей и рассматривается в следующих условиях. Заланы два массива
(а, 5) и соответствующее им количество элементов (mi, 4). Результирующий
массив 7 полжен сопержать и + м элементов. Пол эту область необходимо
выделить память: z=new int [n+m]:
Вводятся три указателя (ра, pb, =): первые два (ра, ob) — Ha за-
данные массивы, третий (=) — на создаваемый массив. Пока не окончится
OUHH из массивов, на каждом шаге пикла. используя разадресацию указателей,
выполняется сравнение элементов, т. е. больший элемент переписывается в
новый массив; при этом текушие указатели. связанные с созлаваемым масси-
вом и массивом-источником, перемешаются к слелующему элементу. После
исчерпания олното из массивов остаток пругого переписывается в конец
нового {побавляется в хвост}. Фрагмент программы, поступ по указателю
int “pa,*“pb,*“pz; // вспомогательные указатели
#==пем int [nt+m];
// Устанавливаются на началс массивов
ВЕ=2; ра=а; pb=b;
//слияние массивов, проверка значений укавателей
46
Глава 2. Способы npedemasienua скнруютую данных. Состаяные мины
Р2==; pata; pbob;
// слияние, цикл до конца одного из массивов
while{ра<atm&&pb<btm)
if (“ра < *pb)
%рЕ++Е*РЬ++;
elge “pzt+=*patt;
// остаток одного из MaccMECE добавляется в хвост нового
if (pb >= btm)
while {ра < atn) *pett="patts
if{pa>аз)
while (pb < btm)
*pet++=*pbtt+;
Удаление вхождений заданных элементов массива. Эту задачу можно ре-
шить с помошью двух алгоритмов.
В первом алгоритме используется рассмотренный выше алгоритм удале-
ния заланного элемента. Этот алгоритм выполнятся всякий раз. когда опре-
пеляются индексы или адреса упаляемых элементов массива по заданному
критерию (например. четных элементов}. Поиск этих элементов осущест-
вляется во внешнем цикле. в котором просматривается исходный массив.
В этом пикле определяются адреса (или индексы) упаляемых элементов.
В лругом апгоритме опрелеляются элементы. не поллежадтие удалению»
{не удовлетворяющие заланному критерию). Они записываются на места уда-
ляемых. Для этой пели применяются опин цикл, но два индекса По одному
индексу просматривается массив и выбирается текуший элемент, по другому
индексу записываются элементы, не поллежашие удалению. что аналогично
задаче переписать заланные элементы в другой массив, но в этом случае в
качестве другого массива оказывается исходный.
Программная реализация первого алгоритма на примере удаления четных
элементов из массива с использованием поступа по индексу:
1=0;
while {i < п)
if (! ali]3 2)
// удаление ali] и уменьшение
числа злементов
For(1-1:j<Ш;++)
а[3]=а [9+1];
в--;
} // уменьшение количества
//если текущий элемент не удаляется, перексд к следующему
else
i++;
Кол алгоритма. в котором на место удаляемых элементов записываются
те. что остаются (используются два индекса i, 1: по одному индексу просма-
тривается массив. по другому индексу остающиеся элементы записываются
на места удаляемых):
for {i=0, 1=0: i < nz itt)
if {'{а[1] % 2))
а[1++]=а[1];
Алгоритмы сортировки массива. Отсортировать массив — это значит
упорядочить элементы по заданному критерию (по возрастанию или по
убыванию). Прямые метопы сортировки — методы. не требующие ввепения
2.4. Адресная арифметика. Указанели и динамические массивы
47
дополнительного массива. Основные прямые алгоритмы сортировки: метод
полного перебора, метод простого обмена (или пузырька), метод вставки. Со-
ртировка метолом полного перебора рассмотрена пля статических массивов.
Сортировка простым обменом. Метод заключается в послеповательных
просмотрах массива и в обмене местами соседних элементов. расположенных
не в соответствии с критерием. Число просмотров массива опрепеляется во
внешнем пикле: пикл выполняется до тех пор. пока не останется ни одной
перестановки. Вводится логическая переменная (признак перестановки). при-
нимающая значение 1/0 или true/false. Из анализа признака перестановки
следует заключение о том. отсортирован ли массив либо необходимо еще раз
выполнить просмотр: если есть хоть одна перестановка, то Е — флаг (признак)
перестановки — принимает заданное значение, и выполняется внешний цикл.
Фрагмент программы сортировки методом простого обмена, использу-
ющий доступ по индексу: внешний цикл выполняется, пока есть хоть одна
перестановка (по тех пор. пока Е остается равным 0):
int Е, buf;
do { Е=0;
// флаг перестановки
For (1=0; i < п1; itt)
i (a[i] > af[itl]) {
БаЕ=а[1]; а[1]=а[2+1]; a[itlL]=buf; f=1; }
Фратмент программы сортировки метолом простого обмена, использую-
ший доступ по указателю: в пикле по указателю р просматривается массив.
сравниваются соседние элементы * {2+1} и *в и переставляются местами:
<епСВ)
Ен1
элементов, используется разадресация
*p=* {pti “pti )—bwt;
Сортировка вставками. Этот метод основан на предположении. что ис-
ходный массив состоит из отсортированной и неотсортированной послелова-
тельностей. В самом начале отсортированной последовательностью считается
первый элемент. В цикле просматриваются элементы части массива, которая
еще не отсортирована и кажлый текуший просматриваемый элемент вставля-
ется на свое место в уже отсортированную последовательность предыдущих
просмотренных элементоЕ на предназначенное ему место.
Каждый просматриваемый элемент в цикле сравнивается с элементами в
отсортированной части, пока в уже отсортированной части массива не най-
дется число меньше ето. Тогда выполняется сдвиг элементов вправо в OTCO-
ртированной части массива и элемент записывается на место вставки. Если
не найдется меньшее значение, обмен не выполняется. В этой сортировке
48
Глава 2. Способы apedemaaienua cmpyemyp данных. Сосктааные мины
элемент меняется местами не со всеми числами Е массиве, а ТОЛЬКО в том
случае, когла выполнится условие вставки (в рассматриваемом случае сорти-
ровки по убыванию — с меньшим элементом). Поэтому сортировка вставками
происходит быстрее, чем сортировка предыдущими алгоритмами. Лля уже
частично отсортированных массивов сортировка вставками — наилучший
алгоритм сортировки.
Далее приведен фрагмент программной реализации сортировки встав-
ками с использованием доступа по индексу (пля нахождения места вставки
очередной элемент сравнивается с кажлым большим его элементом в отсор-
тированной части):
for{1=1;i<п;21++){
// по индекса 1-1 часть массива отсортирована
7=0;
БаЕ=а[1]; // элемент для вставки
// цикл поиска места для вставки
while {(] < 2-1) = (buf » а[7]))
i (but <= af[j]) {
// сдвиг злементов вправо в отсортированной
// части массива для вставки от границы (1-1) по aly
for: ЧЕ
ob
ЕЕ
// сдвиг вправо
а [+1]
=а [Е];
a[j]=buf;
// записывается на место вставки
}
Бинарный поиск. Бинарный поиск заданного элемента в отсортированном
массиве осуществляется метопом деления пополам просматриваемой части
массива. Вволятся две границы: верхняя им’ и нижняя не, определяется ин-
декс среднего элемента массива
ms=(ngtnw) Ир,
Tie ng — начальный инпекс: nw — конечный инлекс просматриваемой части
массива.
Границы зоны поиска изменяются в зависимости от критерия упоряло-
ченности (по возрастанию или убыванию) по правилу отсечения бесперспек-
тивной области для поиска: если элемент в середине не равен образцу. то
исключается бесперспективная часть зоны поиска {либо левая, либо правая)
пугем изменения границ.
Для массива, упорялоченного по возрастанию:
* если alms] < obr, изменяется нижняя гранипа не = му + 1;
* если alms] > cbr, изменяется верхняя граница ни = м5 — 1.
Фрагмент программы бинарного поиска:
int nw, ng, ms, Е=0;
ng=0; // нижняя граница
пи=п; // верхняя граница
cin >> obr;
while(ng<=nw&&12)|
25 Линамыческие мнозомерные массивы
49
ms=({ngtnw)/2; // индекс центра
if (c[ms] == obr) // сравнение с образцом
Е=1: // элемент найден
else
if (c[ms] < obr) // анализ области для поиска
ng=msti; // отбрасывается левая часть
else
nw=ms-1; // отВрасывается правая часть
if (Е) // анализ решения
cout << ” у “<< c[ms]<<" повиция “<<ms<< endl;
else cout <<" не входит “” << endl;
2.5. Динамические многомерные массивы
Выделение памяти. Линамические многомерные массивы создаются в ди-
намической области памяти (куче) на этапе выполнения программы. Общий
полход Е созданию пинамических многомерных массивов рассматривается
на примере создания матрицы (массив из одномерных массивов, каждый из
которых представляет собой строку матрицы). Для кажлой строки определя-
ется указатель, используемый для выцеления памяти под заланное количество
элементов. аналогично олномерному массиву по этому указателю динамиче-
ски выпеляется память под каждую строку матрицы. Поскольку эти указатели
имеют один и тот же тип, они объединяются в динамический одномерный
массив указателей, память поп который выделяется пинамически. Адрес
этото массива указателей хранится в указателе на указатель. Для выделения
памяти под пинамическую матрицу объявляется указатель на указатель на
тип элемента:
«тип компоненты» * *<идентификатор указателя? ;
Число звездочек перел именем илентификатора показывает уровень кос-
венной апресации. который опрелеляет, сколько раз следует выполнить опе-
рацию раскрытия указателя, чтобы получить значение конечной переменной.
Здесь идентификатор указателя» — это указатель на массив указателей.
Например: объявление переменной типа указатель на указатель на int:
int “Фа;
Объявление переменной типа указатель на указатель на тип double:
double **“b;
Далее выделяется память под одномерный массив указателей. использу-
емый для выделения памяти под строки матрицы
«тип компонентыь ‹идентификатор» [<количество строкЪ];
Для интерпретации описания переменной существует правило: «суффикс
привязан крепче префикса». Если при описании переменной применяются
одновременно префикс *‹хуказатель> и суффикс [], то переменная ин-
терпретируется как массив указателей на <тип компонент».
50
Глава 2. Способы представления структур данных. Состаяные знины
Динамические массивы указателей: int*al[n],
double*b[n];
Методика выпеления памяти под матрицу состоит в следующем. Дина-
эпический массив указателей создается с помошью операции new, количество
элементов массива указателей на строки определяется по числу строк:
= указатель на указатель > =
new «тип компоненты» *[«число eTpor>]
Здесь выделяется память в куче и апрес этой области памяти присваи-
вается указателю. Например: объявляются указатели Ha double Haint и
на указатель:
double “*pb ; int ** pa;
Создаются массивы указателей типа указатель Ha double и указатель Ha
int! выпеляется память под массивы указателей. Указатели ph и ра получают
адреса этих областей памяти:
pbhsnew double *[n]; pasnew int *[n];
Tae н — число строк матрицы.
Следующий шаг — выделение в цикле памяти пол строки матрицы Kak
пол олномерные массивы: по каждому указателю операцией new выделяет-
ся память на строку. указателю присваивается апрес начала участка памяти.
На каждом шаге Г цикла доступ осуществляется разадресацией указателя
на указатель **‹идентификатор_указателя>. Разадресация реализуется
одним из трех способов: через смешение от начала области под массив уЕаза-
телей, через индекс. через текуший указатель, который перемешается в ЦИЕЛе:
* {<идентификатор указателя»+1)
<идентификатор указателя» [1]}
*«текущий указатель»
Программный код. в котором выполняется выделение памяти под Ma-
трипу пи. где п, m — число строк и столбцов. которые залаются на этапе
ВЫПОЛНЕНИЯ. Для кажлого указателя на целые выпеляется память под массив
пелых из п элементов. Использование смешения:
For {int 1=0; i < ny itt) “*“(patij—new int[m];
Использование доступа по инлеесу: выделяется память под массив целых
из ш элементов:
1=0;i<nyitt)
pa[i]J=new int[m];
Использование еспомотательного ТЕЕУшШеГО указателя <THO>*p, Е ЛАанном
случае int "ре
int *“p; for {р=%ра, p<*(patn}; ptt) р=пем int[m];
Этот указатель пробегает по массиву указателей Ha строки. на кажлом
шаге цикла он устанавливается на апрес начала строки. Начальный адрес —
адрес начала массива указателей (разадресованный указатель на указатель
*ра). На каждом шаге цикла выделяется память под массив из m элементов.
2.5. Линамические многомерные массивы
51
Освобождение динамической памяти под матрицу выполняется в два
этапа: вначале освобождается память. отвепенная пол каждую строку матрицы:
for (1=0: i < ny itt)
delete [] «указатель на 1-ю crpoRy>;
После Gero освобождается память. занятая массивом указателей:
delete [] «идентификатор указателяз;
Пример освобождения памяти, вылеленной пол матрицу:
Еог [1=0; i < mz itt}
delete [] ра[1];
Освобождение памяти. выделенной поп массив указателей:
delete [] ра;
Доступ к компоненте. Все обрашения в программе к переменной mo ее
имени заменяются компилятором на адрес области памяти. Е которой хра-
нится значение переменной. В памяти многомерный массив располагается
B ПОСЛедовательных ячейках построчно, т. е. массив массивов. Количество
элементов кажлого массива равно длине строки (количеству столбцов матри-
ны). Многомерные массивы размещаются таким образом. что при переходе к
слелующему элементу быстрее всего изменяется последний индекс. Для по-
ступа к отдельной компоненте напо знать HOMEep строки Г по которому можно
определить начало участка пол данную строку и номер элемента в этой стро-
Ke. Кажпый HX ЭТИХ ИНДЕЕСОЕ MOET ИЗМЕНЯТЬСЯ OT 0 ДО < количество>-1'
«доступ к злементу в 1-й строке и 7-м столбиеъ::=
«<идентийикатор» [«номер строкиЪ][«номер ctonhua>]
Эта запись обозначает действия, определяющие механизм поступа, Ko-
торый реализуется следующим алгоритмом. Сначала определяется алрес
строки, потом адрес элемента в строке, а разадресация обеспечивает доступ
к значению. Рассмотрим использование нотации указателей. Алрес строки
хранигся в текушем указателе с номером! в массиве указателей. Ero значение
определяется операцией разадресации, которая реализуется одним из трех
описанных способов. Использование смешения:
«адрес 1-й строки» =
* {«идентификатор указателя на указатель»+
номер {индекс} строки)
Использование инпекса:
«адрес 1-й строки» =
<идентификатор указателя на указатель» [‹индекс строки>]
Использование текущего указателя :
«адрес 1-й строки>-«текущий указатель»
Для рассмотренных выше примеров апрес строки определяется одним
из трех способов:
* (ра+1); peli];
где р — текущий указатель.
52
Глава 2. Способы представления структур Занных. Состаяные мины
Для лоступа к элементу в текушей строке в ней как в опномерном массиве
Halo опним из следующих способов вычислить адрес элемента с заданным
индексом и его разадресовать:
1. Использование смещения относительно начала массива:
<«злемент 71-го столбца 1-й строки» =
* [* {<«идентификатор указателя»+т<индекс строкы L>)+
«индекс столбца j>)z-*“(*{pati) +]};
2. Использование текушего указателя строки. который устанавливается
на начало строки 1-й строки:
=]-й элемент 1-й строки>- “(текущий указатель строкиь +
«индекс столбца 7>)-—“{р+7);
3. Использование доступа по индексу пля /-й строки ali]:
=]-й элемент 1-й строкиЪ=
+ {<идентификатор указателя» [«индекс строкиз]+
<индекс cTonhua>)-— *{ра[1]+));
4. Использование текущего указателя <тип компонента>*ре;, который
сначала устанавливается на начало /-й строки pt=a [i], азатем проходит по
элементам текущей строки:
<]-й элемент 1-й строки>=“стекущий указатель строки»
5. Доступ по индексам: <]-й элемент 1-й строки» =
<идентификатор указателя» [‹инлекс строки 1>]
[<индекс столбиа j>]
Например: ра[1] [jl];
Для всех рассмотренных выше примеров /-Й элемент /-it строки после-
довательно определяется как
* {* {ра+1} +1); *(ptj); *(pelilt+j}; “pt; palil{j]-
Далее используем доступ по индексам как наиболее простой.
Программные конструкции пля иллюстрации рассмотренных попхолов
приведены на примере запания значений матрицы ра в препположении, что
память выделена под и строк. м столбиов и объявлены переменные int i,
ЗЕК
Использование доступа к элементу по индексам:
For {i-0, K=O; 2.
п itt}
For{j-0;9<ms;ПЕ
pa [i] [j]=k++;
Использование достуна по указателю с использованием смещения, где
*(pati) — адрес начала одномерного массива (строки матрицы): + (pat
+1} +) — адрес элемента ра [1] [-]] в этом массиве: сам эпементра [i] [1] —
разадресованный указатель — * (* {ра+1}+7):
For [1=0, k=;
Пе” ТЯ itt}
For {j=-0;7; j = mz ++)
“(*{patijtj} ЕЕ;
2.0. Алгоритмы и программы обрадбоннки duvaMitecnWX Mapu,
43
Использование достуна к элементу но индексу а строке и но указателю в
столбые, Tie ра[1] — апрес начала строки матрицы: {ра[1]+7} — ampec
/-то элемента в строке " элемент ра[1] [7] — разадресованный указатель
*{ра[1] 17}:
for (1=0, k=O; i < nz itt)
fer {37-0; j < m; J++)
“(pa [11+] =Е++;
Использование доступа к элементу по текущему указателю, тде объяв-
ленный указатель на указатель вида <тип> **‹идентификатор> получает
адрес непрерывной области памяти в куче, выделенной под матрицу. Этот
илентификатор является именем динамической матрицы. Значение указа-
телей на начато вылеленной области не должно изменяться. их необходимо
сохранить. Изменяться может другая переменная. вспомогательный теку-
птий указатель TOTO же типа, которому присваивается апрес начала области:
int “pt. Он вычисляется косвенно через указатель на массив указателей на
строки матрицы int **ра. int **р. Для доступа по текущему указателю
используются два указателя int “pt Hint *“*р, где р бежит по массиву
указателей на строки (апрес строки — разадресованный указатель *р), pt
бежит по строке.
Во внешнем цикле определяется текущий апрес строки *р. Во вложенном
цикле изменяется указатель на элементы матрицы, который сначала устанав-
ливается на начало строки (25=*р), а затем перемещается к слепующему
элементу в строке, как в опномерном массиве. Указатель pt устанавливается
на начало строки pt="p и бежиг по строке — pt++. Элемент матрицы опре-
деляется через разапресованный указатель *ръ.
Фрагмент кода (р бежит по массиву указателей на строки):
int *“pa,
ea Sone Spire fant ake
for {popa, k-O; р < patn; ptt}
for {pt="p; pt < “ны; ptt++}
// pt устанавливается на начало строки
“pt=ki+; // 4 pt- элемент в строке;
Апрес в памяти начала массива, соответствующего текушей строке. опреде-
ляется разапресацией указателя р на эту строку: Pt=*p, pt, который во вложен-
HOM цикле перемещается по элементам строки pt++, *pt — значение элемента.
соответствующего ра [i] [1].
Доступ K элементу по текушему указателю осушествляется быстрее по
времени и эффективнее, чем по индексу
2.6. Алгоритмы и программы обработки динамических матриц
Можно кылелить два типа классических задач обработки матриц: обра-
ботка всей матрицы, обработка по строкам и столбцам. Далее рассматрива-
ются базовые задачи. решение которых обеспечивает решение общих залач.
54
Глава 2. Способы представления структур данных. Составные мины
Задание значений элементов матрицы может быть выполнено следующи-
ми способами: вводом с клавиатуры, чтением из массива констант, моделиро-
ванием, формированием из элементов других массивов. При инициализации
многомерный массив констант либо представляется Kak массив из массивов
{при этом каждый массив заключается в свои фигурные скобки, в этом слу-
чае левую размерность при описании можно не указывать). либо задается
облтий список элементов в том порядке, в котором элементы располагаются
в памяти. Инициализация матрицы констант по строкам:
double be[] [S]={{4
{1,5,8,9,3}, [24,6 é, '
int: yell aj, 14,5,4,245, 19, 32, 1,3) 21,7 pb, 29:
paps a Lee {lS 745,07 вх 945 f3,7, 4,3, bel;
Шаблон лля создания и обработки динамических матрии:
#include <iostream>
using namespace std;
oid main(}) {
ik: УБЫВ
AeSeooShaeaoeey
ry tence констант
if объявление указателей на указатели матрицы
int *“c,
“а;
// объявление указателей-массивы
int “104,
Е Spey float “we;
// вспомогательные переменные
int ky te: Че Abe imax, тах, max; float в, butl;
cin>>n>>m;
// пля стладки n=2; m=2;
// Выделение памяты под динамическую матрицу n*m:
а=пем int*[n];
// выделение памяты под массив указателей
for{=iжоаеatt)
/’ используется доступ по индексу
[m] ;
// выделение памяти под строки матрицы
// ввод матрицы по строкам
for {i=0, k=O: i < п: itt)
for:О a-<m; 44+}
cin>> ali][j]) ;
// Выделение памяти под динамическую матрицу n*m:
с=пем int*[n];
for { 1=0; i < п; 1++) cl[i]J=new int [1];
а[1]=печ int
// чтение из массива констант, К - индекс массива
for {i=0, k=O; a.
п: itt)
For {j=0; 3 < m; 44+}
eli] [4])=y[kt+]; // Е - индекс одномерного массива
// вывод матриц по строкам
for {i=0; i < п; 14441
2.6. Магоринмы и программы обрабоннки Эннамическых Ma REY
55
for {j-0; j < mu; j++)
cout << а[1][14] == TM
a
cout << endl;
}
1 // конеы Вазовой заготовки
Программы для решения задач приволятся как фрагменты рассмотрен-
ного выше шаблона в предположении данных там описаний переменных.
Используется поступ к элементу по индексам.
При решении поисковых запач, требующих обработки всей матрицы,
применяются простые переменные пля сохранения результатов обработки.
Обработка всей матрицы по заданному критерию. Рассмотрим ее на примере
вычисления среднего значения элементов. кратных 3 или 5. Лля определения
среднего определяются количество в переменной A и сумма в переменной $
чисел, запаваемых критериями отбора в условном операторе. Если количество
больше нуля, то среднее вычисляется как сумма. разделенная на количество:
For {i=0, k=0, в=0; 1 < ny itt)
For{j=0;4<m;++)
Е
1 (&[2][59] $3} | РЕ afa]fjJ % 5) }
{st=ali] [3]; К++;}
if('k)в/=k;
При инициализации во внешнем цикле используется операция «запятая».
задаются начальные значения суммы и счетчика.
В целях поиска максимального элемента матрицы (при использовании
доступа к элеменгу по индексам) положение элемента матрицы определяется
индексами его строки imax и столбца тах, а максимальный — a [imax]
[imax]. Во внешнем цикле иницпиализируются начальные значения индек-
COB элемента. На каждом шаге текуший сравнивается с максимальным. Если
текуший больше. то индексы изменяются’
for {i=0, imax=0, jmax=0; i < п; itt)
for {j=0; 9 < m; j++}
if
(а [11 [7] > а[1тах] [jmax]
}
{ imax=i; jmax=j;}
Обработка матриц по строкам и столбцам. При решении задач обработки
строк и столбов матрицы для сохранения результатов обработки использу-
ются одномерные массивы.
Рассмотрим запачу поиска инпексов столбцов максимальных элементов
в каждой строке матрицы. Если используется доступ к элементу по индек-
сам, то положение элемента матрицы определяется его индексами строки
и столбца. Лля определения положения максимальных (или минимальных)
элементов в каждой строке (столбие) постаточно определить инлекс столбца
(строки) элемента, по которым опрелеляется экстремум. Если jmax — индекс
столбца максимального элемента /-й строки, то максимальный в !-Й строке —
ali[l[qmax]. Для использования в дальнейшем полученных результатов тре-
буется их сохранение в пинамическом массиве (например, v), каждый элемент
v[i] которого содержит индекс столбиа максимального элемента строки.
Сам экстремальный элемент строки определяется по значению a [i] [v[i]].
56
Глава 2 Способы представления струклную данных. Cocrhdaaiate Mun
Фрагмент программной реализации для использования доступа по ин-
дексам в предположении. что память под матрицу выделена:
int “*“voenew int[n];
for (1=0: i < nz itt}{
// поиск индекса максимального элемента в 1-й строке:
for {j=0, ч[1]=0; j < my; 4t+t)
if (8[11[5] > alil[v[il])
м.
Внешний цикл задает индекс строки, во вложенном реализуется задача
вычисления индекса первого экстремума в строке. Вывод максимальных
элементов строки по инцексам столбиов. записанных в массив т:
for (1=0; i < п; itt)
cout << ali] [w[i]] << endl;
Обход матриц по диагоналям и контурам. Варианты обхода квадратной
матрицы: по диагоналям, параллельным главной и побочной: по контурам
матрицы: по спирали от центра или к центру с учетом направления обхода.
Для реализации алгоритмов необходимо выявить закономерность формиро-
вания индексов, взаимосвязь индексов строк и столбцов. Индексы элементов
главной и побочной диагоналей выражены один через другой, что позволяет
для этих элементов использовать один цикл. Ниже рассматриваются способы
реализации вариантов обхода.
Обработка элементов матриц, лежаших ниже или выше главной либо по-
бочной диагонали. между главной и побочной диагоналями. или другие воз-
можные варианты выбора. определяются диапазонами изменения индексов.
Если! /— индексы строки и столбца соответственно, то во всех случа-
ях индекс строки / изменяется от 0 дон — 1. Диапазон изменения индексов
столбиов приводится ниже. Индекс столбиа / под главной диагональю изме-
няется от 0 ло /, выше главной — от! дон — 1. под побочной индекс столб-
на/— от n—1+i/ дон- 1, над побочной / изменяется
oT0Ton—i-1.
Краткая запись изменения индексов столбца:
* ниже главной диагонали: / = [0. J):
* выше главной диагонали: / = [i, н-1]:
* ниже побочной диагонали. /= [п —1—i, a — 1):
* выше побочной диагонали: /= [0, п 1—1]:
С использованием этих правил для вычисления индексов рассматрива-
ются фрагменты программ заполнения матриц.
Ниже главной диагонали:
for (1=0: i < п; itt)
for {j=0; 9 <= i; j++)
ali] [3]=1;
Выше главной диагонали:
for (1=0: 1х ny itt)
for: НЕЕ] < amg att)
2.6. Allen pU и прюараммы обрабоннки Эынамическиых ME
a7
Ниже побочной диагонали:
for {i=0; i <=0-1; i++)
Far т
aoene++)
а [1] [9]=4;
Выше побочной диагонали"
Обхол и обработка матрицы по лиагоналям. Число диагоналей матрицы,
параллельных главной или побочной, зависит от ее размера и равно nn — 1.
Число элементов Ha К-й диагонали равно н — К — 1. Рассмотрим связь ин-
дексов для запанной диагонали. Если нумерация диагоналей начинается от
главной. то индексы строк и столбцов, выраженных через индекс строки Ги
номер диагонали. для К-й диагонали, параллельной и выше главной. связаны со-
отношением afi, it+k] , под главной диагональю — соотношением afi, i-k).
Фрагменты программ заполнения матрицы по диагоналям рассмотрены ниже.
Занолнение матрицы но днагоналям, параллельным главной, ниже ее (вклю-
чая главную): внешний цикл — по числу диагоналей, внутренний — по эле-
ментам на ней:
for{k=0;Е<n;Е)
for (1Е>ЕК: i < my itt)
a[i] [i-k]=k;
Заполнение матрицы но диагоналям, параллельным главной, выше ве (еклюшая
главную). Внешний пикл по числу диагоналей, внутренний по элементам на
ней, индекс строки для которых изменяется от 0 доп — Е Каждой диагонали
присваивается номер диагонали:
for {k=1; Е < n; ktt)
for{i=0:i<пitt}
ali] [itk]=k;
Занолнение матрицы по дыагоналям, параллельным побочной диагонали. Нап
побочной диагональю находится (н — |)-я диагональ. и пол ней (п — 1)-a
диагональ. Число элементов на А-й диагонали равно я — А. При нумерации
пиагоналей (отсчет отглавной диагонали) индексы строк и столбцов дляА-йдиа-
гонали над главной лиагональю связаны соотношением а[1] [n-k-i]. Ин-
дексы под главной диагональю связаны соотношением а [1] [п-1-1+Е].
Заполнение матрицы по пиагоналям, выше побочной (вЕЛЮчЧая ee):
for {k=1; Е <= п; kt+)
for([1=0:i<пЕ:i
а[1] [n-k-i]=k;
Заполнение матрицы по диагоналям ниже побочной, включая ее:
for (ЕТ,
Е<п;k++)
for
}1<npitt)
а[1] [n-1-itk]=kt1;
58
Глава 2. Способы представления структур данных. Состааные мины
Обход квадратной матрицы по контурам или спирали. Рассматриваются
варианты обхода матрицы по спирали, которая может начинаться с од-
ного из элементов в углах матрицы а[0] [0]. а[п-1] [9], а[п-1] [1-1],
а [0] [2-1] или раскручиваться из центра а [п/2] [n/2] в разных
направлениях.
В первом фрагменте программы реализуется алгоритм обхола по спирали
к центру. начиная с первого элемента а[0] [0].
Число KOHTYPOB Евапратной матрицы n*n равно n/2. Внешний OKI
выполняется по числу контуров, номер конггра k изменяется от 1 до п/2.
Кажлый конг/р — это попматрица, у которой на лее строки и на два столбца
меньше, чем у предылущей подлматрицы. Обход кажпого контура (попматри-
цы) реализуется в четырех вложенных циклах: по столбцу вниз, по строке
вправо. по столбцу вверх. по строке влево. В каждом вложенном цикле счет-
чик / пробегает по числу элементов на стороне контура (столбиа или строки).
Этот счетчик определяет индексы элементов. которым присваиваются значе-
ния. Возможна обратная запача — чтение элементов. Фрагмент программы,
реализующий алгоритм обхода матрицы по спирали:
for {k=0, 1=0; Е < n/fZ; k++) { // пе числу контуров
for {(j=k; 1 < mk; G++, 144+) // по столбцу BHMS
ax [4] [k]=1;
|
for ()=Е+1;: j « n-kyp jt+, 1++} // по строке вправо
ах [п-1-Е] [71] =1;
for {j=n-l-k; 7 > Е; 1--, 1++} // по столбцу вверх
ах [-]-1] [n-1-k]=1;
for []=1-1-(%+-); ] > Е; j--, 1+Н // пе строке влево
ax[k] [31=1;
Реализация этого алгоритма позволяет вычислить сумму элементов по
каждому контуру (каждой подматрице). Внешний цикл выполняется по числу
контуров. номер контура К изменяется от 1 до н/2. В кажлом из внутренних
циклов по /. который пробегает по числу элементов на стороне контура
{столбца или строки}, определяются индексы элементов Ha противополож-
ных сторонах контура (соответственно пля строки или столбиа) и вычисля-
ется сумма элементов. Сумма по текушему контуру записывается в массив.
Фрагмент программы. реализующий алгоритм вычисления суммы элементов
всех подматриц (KOHTYPOB):
for {k=0, 1=0, в=0; Е = п/а: ktt+) {
for {(j=-kr 3 < ЕЁ 34+)
s+= {ах [4] [k]+ax[j] [n-1-k]); // цикл по строкам контура
for (j=k+1; j<n-k-1; j++) // цикл по столбцам контура
в += {ax[k] [[]+ax[n-1-k] [4]);
=
ry
г ef запись в массив
27 Задания для самостоятельного выполненыя
59
2.1. Задания для самостоятельного выполнения
Задание 1. В опномерном массиве найти индекс первого минимального и
последнего максимального элементов. Вычислить срепнее значение положи-
тельных чисел межлу минимальным и максимальным элементами, вЕЛЮЧаАя
оба этих числа.
Залание 2. Введены два массива. Сформировать новый массив. состоящий
из неповторяющихся элементов лвух массивов (пересечение).
Задание 3. Уталить из массива повторяющиеся элементы и найти частоту
вхождения каждого.
Задание 4. Отсортировать элементы массива между первым минимальным
и последним максимальным элементами.
Залание 5. Отсортировать четные элементы массива. не изменяя порядка
элементов.
Задание 6. Удалить из массива все нулевые элементы. оставшиеся отсор-
тировать по возрастанию.
Задание 7. В каждой строке матрицы ни целых чисел найти количество
элементов. кратных 3 или 3, и записатьих в массив. В этом массиве опреде-
лить номер строки с максимальным количеством заданных элементов и эту
строку упапить.
Задание 8. В кажлом столбие матрицы и* пелых чисел найти количество
четных элементов и записать их в массив. Определить номер столбиа с мак-
симальным количеством элементов. Всем элементам этого столбца матрицы
присвоить значение суммы элементов в этом столбце.
Задание 9. Найти максимальный элемент матрицы н*м. его индексы
строки и столбца. Все элементы, равные ему. заменить на сумму предше-
ствующих элементов.
Задание 10. Лля каждой строки матрицы N+) все четные элементы ма-
триды заменить на минимальный элемент строки.
Задание 11. Удалить из матрицы все нулевые строки.
Залание 12. Отсортировать нечетные строки матрицы методом полного
перебора. четные — методом простого обмена.
ГЛАВА 3. ПРИНЦИПЫ РАЗРАБОТКИ ПОДПРОГРАММ.
ПОДПРОГРАММЫ ФУНКЦИИ С++
При проектировании и разработке сложных программных систем ис-
пользуются IBA полхола: объектно-ориентированная (ООД) и процелурная
{алгоритмическая) декомпозиция. Эти метопы различаются критериями де-
композиции. В объектно-ориентированной критерием декомпозиции системы
служит принаплежность ее элементов к разным абстракциям ланной систе-
мы. Система проектируется как совокупность взаимолействующих объектов.
Процпедурная (алгоригмическая) лекомпозиция предполагает декомпозицию
задачи на подзалачи. на следующем уровне иерархии лекомпозиции каждая из
ползадач подразделяется на подзалачи следующего уровня и так по ползадач,
решение которых элементарно. Инструментальными средства ООЛ являются
объекты. а в процелурной лекомпозиции используется механизм полпрограмм.
В ланной главе рассмотрены процепурная лекомпозиция и инструмен-
тальные средства (подпрограммы. объекты. мопули) ее реализации в програм-
мировании при решении запач обработки линамических стругур ланных.
3.1. Принципы разработки подпрограмм. Интерфейс подпрограммы.
Механизм передачи параметров. Области видимости
В пропедурной декомпозиции используется разделение алгоритмов. каж-
пый из которых реализуется как отдельная ползапача. На уровне отлапки пол-
задачи следующего уровня заменяются протразсыными заглушками, которые
при сборке системы заменяются независимо полученными решениями. Этот
метод разделения по алгоритмам соответствует методу пошаговой летализа-
UHH, или метолу нисхоляшего проектирования. В этой части обсужлаются
принципы разработки подпрограмм, общие для всех пропедурных языков.
Подпрограммы представляют собой инструмент. с помошью которого
любая программа может быть разбита на ряд не зависимых друг от друга
частей. Решение KATO подзалачи можно реализовать как отдельную про-
грамму но при сборке всей системы ползалача полжна быть прелставлена в
випе подпрограммы. Исхоля из принципа современного программирования
{принципа модульности) поппрограммы или объекты объединяют в отдель-
ные модули (библиотеки). которые можно подключать к любой программе.
Каждый молуль представляет собой файл, пля которого возможна отдельная
3. 1. Иринциты paspadomiu nodapaspaaw. Интерфейс подпрограммы...
61
от других модулей компиляция. Количество используемых модулей ограни-
чивается лишь возможным объемом памяти.
Общим для всех процепурных языков программирования является прин-
UHI инкапсуляции: скрытие деталей реализации. Подпрограмма рассматри-
ВАЕТСЯ Kak «черный ящик». взаимодействие с которым осуществляется через
интерфейс. определяемый ее заголовком.
Содержание интерфейса подпрограммы: HMA, входные и выходные пара-
метры. Имя. входные и выходные параметры входят в заголовок полпрограм-
мы. Как решена залача лля вызывающей программы. не имеет значения. важно
знать. что HATO полать на вход и что получить на выходе. Подпрограмма —
последовательность описаний и операторов. реализующая определенный
алгоригм и имеющая имя (заголовок). через который она может принимать
входные значения и возврашать значения. Имя подпрограммы используется
в качестве сокрашенной записи там, где встречается эта последовательность
действий. Вызов подпрограммы осушествляется оператором вызова подпро-
граммы. Для вызова необходимо указать ее имя и передать список параметров,
соответствующий спискам входных и выходных параметров. Перепаваемые
вхопные параметры лолжны иметь значение. а выхолные — получать значе-
ния. Полпрограмма полжна быть описана и помешена в программный файл
или отдельный молпуль. Синтаксис определения полпрограммы содержит две
части: заголовок и тело (блок). которое включает описания, и операторы, pea-
лизующие алгоритм, в предположении, что вхолные известны, а выходные
передаются при вызове:
«объявление
поппрограммег» 1: “3aTonoBboR?<Tenlo >
Заголовок подпрограммы включает следующие элементы"
* ЕЛЮЧЕВОЕ СЛОВО ЯЗЫКА <подпреграмма>:
* имя полпрограммы:
* список формальных параметров, через которые осуществляется обмен
информацией межлу вызываемой и вызывающей полпрограммами'
<список формальных параметров»: : =
{[еписок входных параметров>], [<список выходных
параметров» }
< тело п/п >::=[«‹раздел описаний>][<разлел сператоров>]
Формальные параметры — это параметры. объявляемые в заголовке под-
программы и соответствующие входным и выхопным параметрам. Список
формальных параметров рассматривается как расширение разпела описаний,
все переменные из этого списка применяются в подпрограмме. Переменные,
описанные в подпрограмме. называются локальными параметрами. Их имена
действительны только в подпрограмме, в которой они описаны. Переменные,
описанные вне подпрограмм, доступны ей и являются глобальными для этой
подпрограммы. После своего описания подпрограмма может быть вызвана в
любом месте блока, в котором она видима.
Оператор вызова подпрограммы:
«имя подпрограммы» {[«список аргументовЪ]);
«переменная»: :=<имя подпрограммы» ([<cumcox аргументов»]);
62
Глава 3. Ирыиципы разработки подпрограмм. Годнрюзраммы функнии C++
Переменные, которые передаются подпрограмме при вызове. должны
соответствовать по порядку и типу формальным параметрам. Подпрограммы
могут многократно использоваться в разных точках программы или в разных
модулях путем обрашения к ней. Вызов подпрограммы осушествляется упо-
минанием имени подпрограммы в операторе вызова и перепачей аргументов.
соответствующих входным и выходным параметрам. Эти параметры назы-
ваются фактическими параметрами или аргументами, передаваемыми при
вызове. Входные параметры передаются в подпрограмму через выходные
параметры возврашаются результаты выполнения подпрограммы.
При вызове текуший адрес выполняемой программы (адрес возврата)
«проталкивается» в стек по принципу «последним вошел, первым вышел»
(last in, first out, РО). Адрес возврата, помешенный в стек, позволяет функ-
ниям возврашать управление туда, откуда они вызваны. Благодаря этому
принципу текушая функция извлекает последний апрес возврата, записан-
ный в стек. после чего продолжается выполнение вызывающей или главной
функции.
Механизм передачи параметров. До тех пор пока подпрограмма не вы-
звана, память под ее локальные переменные и формальные параметры не
выделяется. Под эти переменные она выделяется в области памяти стека в
момент вызова подпрограммы. После выполнения подпрограммы память
стека под переменные и параметры подпрограммы освобождается и все зна-
чения теряются.
Сушествуют два способа передачи параметров в подпрограмму: по зна-
чению и по адресу Механизмы передачи входных и выходных параметров
{аргументов} различаются способом выделения памяти под аргументы.
В первом случае способ соответствует передаче входных, во втором — передаче
выходных параметров. Список фактических параметров при вызове должен
соответствовать по типу и по количеству списку формальных параметров.
Кажлая переменная имеет HMA, тип. значение. указатель (адрес в памяти).
Указатель — это переменная, значением которой является адрес переменной.
Память пол переменные поппрограмм выделяется в момент вызова в стеке
и существует, пока выполняется поппрограмма, т. е. эта память временная.
По завершении подпрограммы она освобождается. В этой временной памяти
выделяются области под локальные переменные и под входные параметры
{соответствующие им формальные). в которые копируются фактические пара-
метры (аргументы) из вызывающей программы. а также область пол указатели
{адреса аргументов), соответствующие выходным параметрам.
Передача параметров, при которой во временной памяти выделяется па-
мять под копии аргументов. называется передачей но значеныю. Один из важ-
нейших вариантов временной памяги заключается в использовании области
стека. При передаче по значению в стек заносятся копии значений аргумен-
тов. в коде подпрограммы используются эти копии. Подпрограмма работа-
ет с копиями входных параметров. Доступа к области памяти аргументов у
подпрограммы нет, отсутствует и возможность их изменить. Использование
такого механизма передачи параметров не позволяет возвратить результаты
{выходные параметры) в вызывающую программу. все данные теряются.
3. Приннины pazpadomiu поднроаралм. Hmendetic нодпрограммь...
63
При передаче параметров по значению факгическим параметром может
быть выражение того типа. которое соответствует типу формального параметра.
При вызове вхолными параметрами, являющимися простыми переменньсми. в
качестве аргумента могут использоваться константы, выражения. переменные.
При вызове вычисляются выражения, стоящие на месте входных параметров.
в стеке выделяется память под формальные параметры в соответствии с их
типом, и каждому из них присваивается значение соответствующего аргумента.
Основной способ для передачи выходных аргументов из подпрограммы —
нередача по адресу. При передаче по апресу в стек заносятся адреса аргументов,
подпрограмма имеет поступ к ячейкам памяти по этим апресам и может из-
менять исходные значения аргументов. В этом случае подпрограмма работает
не с копией аргумента в стеке, а с областью памяти аргумента (фактического
параметра). которая не освобожлается при завершении подпрограммы.
При вызове полпрограммы во временную область памяти стека перелают-
ся адреса аргументов, соответствующих выходным параметрам. Подпрограм-
ма имеет достиг K этой памяги, она работает по этим адресам с аргументами
в области памяти вызывающей подпрограммы и может изменять значения
этих областей. Аргументы. соответствующие выходным параметрам, могут
быть тольо переменными. по апресу которых определяются ячейки области
памяти, где остается результат выполнения подпрограммы.
Области вилимости переменных. Области видимости, или области пей-
ствия. переменных определяют доступность переменных (идентификаторов)
в программном файле в соответствии со следующими принципами:
1. Объекты. объявленные в поппрограмме фунЕПиИИ или в блоке, дей-
ствительны в этом блоке и BO всех входящих в него блоках. Ho эти имена
неизвестны в охватывающем блоке. Такие переменные называются локаль-
ными. Они создаются при входе в функцию и уничтожаются при выходе, не
сохраняя значения межлу вызовами. Код функции — блок операторов закрыт
2. Принцип локализации: если в подпрограмме заново описывается ипен-
тификатор, уже описанный в блоке. то в полпрограмме и во всех входящих в
Hee блоках действуют новые описания: текуший илентификатор перекрывает
старое описание. Старые описания бупут действовать после завершения поп-
программы, когла вычисления вернутся в охватывающий блок. К ним можно
получить доступ, используя описания пространства имен.
3. В блоках опного уровня любой илентификатор лолжен быть описан до
его использования. для блоков одного уровня видимо (доступно) то. что опи-
сано выше, а что описано ниже — невицимо (недоступно). Для того чтобы
каждая подпрограмма могла вызвать любую, независимо от места описания,
используется опережающее описание — прототип или сигнатура (заголовое
подпрограммы. в которой указываются типы всех формальных параметров).
4. Глобальные переменные должны размешаться вне всех фунеЦИЙ в
самом начале программы. Они доступны в любом блоке. но занимают память
все время выполнения программы. Использование глобальных переменных
ослабляет независимость фунЕПий.
5. Основной принцип взаимодействия подпрограмм — передача аргу-
ментов через механизм формальных параметров. В случае использования
тлобальных параметров теряется свойство независимости подпрограммы.
64
Глава 1. Нринцины разрабонкы поднрограмм. Поднразраммы функции C++
Интерфейс полпрограммы. Распределение памяти. Взаимодействие между
функциями осуществляется через интерфейс. определяемый сигнатурой
функции. который включает в себя параметры и возврашаемое значение.
В список формальных параметров (в интерфейс) выносятся входные и вы-
холные параметры, рассматриваемые подпрограммой как расширение раздела
описаний. Все переменные из этого списка используются в подпрограмме.
Если формальный параметр объявляется Kak параметр-значение, то при
вызове функции в стеке создается копия соответствующего формального
параметра. Все изменения копии не меняют фактического параметра. Если
программе требуется изменить значение параметров функции. программа
передает в функцию адрес параметра (передача по указателю или ссылке).
В этом случае функция работает непосредственно с областью памяти аргу-
мента из вызвавшего блока и может его изменить.
3.2. Разработка подпрограмм в С++. Функции
Полпрограммы в C++ реализуются как функции. Вложения функций не
попускается — все функции одного уровня. С каждой функцией связаны три
понятия: определение {или описание). прототип и вызов фунЕции. Каждая
объявленная функция должна быть определена в программе, но не внутри
пругой функции. Опрепеление функции, в котором выделяются заголовок и
тело. имеет слепующий формат:
«описание функцим» ::= «заголовок Функции» «тело функции»
«заголовок функции» := «тип возвращаемого значения» «имя
функции» ([comcon формальных параметров])
=спыео к формальных параметров»: i=
=список спецификаций сотлельных параметров»
«спецификация > 1:= <TH параметра» <MMA NapaMerpa>;
тип воэзвБащаемого значения» Lis
Тип возврашаемого функцией значения — любой базовый или произвол-
ный тип. указатель или void. Использование указателя позволяет возвращать
переменные производного типа. Функция не может возвращать массив или
функцию, но может возвращать указатели на любой тип.
Тело функции — это блок, т. е. послеповательность описаний и операто-
ров. заключенная в фигфные скобки:
«тело функимыи или блок» ::= {
[раздел списаний>| [ «операторы»]
тебитп «идентификатор переменной»]
a
r
Оператор return обеспечивает возврат значения. полученного в функ-
ции. тип илентификатора переменной определяется <типом возкращае-
мого значения». В функциях. не возврашающих значения (аналогичных
процелурам). оператор return He нужен.
32 Разработка sodapoapan в С++. Фрикыми
65
Определения используемых функций могут следовать в любом порядке.
но до вызова функции следует поместить прототип функции. Прототип
функции — явное объявление функции, которое прелшествует опрелелению,
он совпадает с заголовком ее опрепеления (сигнатуры). В конце описания
прототипа обязательно наличие точки с запятой. Все функции имеют один
уровень. вложения функций не допускается.
Структура программы в консольном приложении — файл с описа-
ниями функций. Главная ФунЕПИЯ — main(), с которой начинается вы-
полнение программы. Описания функций размешаются после описания
главной функции. прототипы функций (заголовки) помешаются перед
главной функцией. после директив подключения библиотечных файлов.
В главной функции реализуются вызовы подпрограмм — функций, кажлая
из которых может вызывать другие функции.
Шаблон струкгуры программы с подпрограммами-фунециями:
/ / лирективы процессора
'/ прототипы пользовательских функций
«прототип заголовок 1>;
«прототип заголовок 2>;
'/ прототипы других функций
«прототип заголовок n>;
#/ главная функция
void мазо ()1
«тело главнсй функции»
I
// описания Функций
«тип» «имя1» (параметры
<тело Функции i>
}
«тип» «имя?» (параметры
«тело Функции 2»
}
// описания других функций
«тип» «имя п» (параметры} |
<тело @ункции n> }
Описание функций допускается помещать перед главной функцией. но
в этом случае функции. описанные выше. не виляг функций. описанных
ниже. и не могут их вызывать. Имена функций, как имена внешние. полж-
ны быть уникальными среди других имен файла. в котором используются
функции. Формальные параметры — переменные. которые принимают зна-
чения. передаваемые функции при вызове. и переменные, через которые
возврашаются результаты. Они рассматриваются как расширение раздела
описаний. Параметры, объявленные внутри функции, называются локальными
(автоматическими). Область действия переменных отраничена функцией, в
которой она объявлена. Память под локальные переменные выделяется в
стеке вместе с апресами возврата. По принципу локализации другие функции
66
Глава 1. Aung разработки подпрограмм. Тоднрограммы функции C+
могут объявлять локальные переменные с такими же именами. Лля фУНЕЦИЙ
с возврашаемыми значениями в блоке функции необходимо присугствие
оператора return
Feturn < выражение No
где тип выражения определяется типом возврашаемого значения. Если ис-
пользуется несколько операторов return (выбор из альтернатив). то функ-
ция завершается по выполнении первого оператора return и управление
передается следующему за вызовом оператору Если функция не возвращает
никакого значения (THT void), то оператор return опускается.
3.3. Вызов функции. Передача простых переменных
Для вызова функции Halo указать ее имя и передать параметры:
«имя функции» |[«еписок аргументов»|);
Для функций с возврашаемыми значениями вызывающей функции воз-
вращается значение переменной типа фунеции. Это может быть простая пе-
ременная, указатель или переменная пользователького типа. Значение может
использоваться в качестве операнла в любых выражениях и в частном случае
присваиваться переменной:
«переменная вызывающей функции >=
<имя функциы» ([<список аргументов>]);
Функция возвращает значение. копируя его в память или Е опин из ре-
гистров процессора. Прототип функции сообщает вызывающей программе
тип данных. Если типы в вызывающей и вызываемой функциях согласова-
ны. то вызывающая программа читает это значение. Если фунЕкпия ничего
не возврашает (тип возврашаемого значения void), вызов определяется как
<вызов функции> ::= ‹имя функции» ([<список аргументов? ]) ;
Аргументы, или фактические параметры, должны соответствовать по
порялку и типу формальным параметрам. При обрашении к функции фор-
мальные параметры заменяются фактическими при соблюдении соответствия
параметров по типам. Аргументы передаются из вызывающей программы в
функцию по значению. адресу или ссылке.
Передача параметров по значению. Простые переменные. При передаче по
значению параметры функции принимают копии значений перелаваемых
аргументов, память под которые выделяется в стеке. При выходе из фунЕции
CT&E освобозжлается. их значения теряются безвозвратно. Передача по значе-
нию в языке C++ принята по умолчанию. соответствует передаче входных
параметров и целесообразна при перелаче простых переменных. В этом случае
затраты времени на копирование и память незначительны.
Описание формальных параметров при передаче по значению:
<секция входных параметров» ::= «имя типа> «идентификатор
переменной» ([,<MMH типа» «‹идентификатор переменной>]
33 Вызов deen. Передана apocrine переменных
67
Функция. возврашающая значение, копирует его в один из регистров
пропессора, откуда его читает вызывакитая функция в переменную соот-
ветствующего типа (тип возврашаемого значения) Формальные параметры,
соответствующие выходным параметрам. передаются по ссылке. Этот спо-
соб перепачи осушествляется использованием указателя на месте выходного
параметра:
«секция выходных параметров» ::= <MMA типа» “идентификатор
переменной» ([,<MMH типа> *“<‹идлентификатор переменнойъ|
Передача простых переменных как вхолных значений, возвращаемое значе-
ние функции. Рассмотрим пример фунеции. вычисляющей значение линейной
функции. Вхолные параметры — коэффициенты A, } и точка х — передаются
по значенику
double lin {double k, double b, double x)
return k*xtb; }
Главная функция. в которой осуществляется вызов:
void main(void) {
double a, b,
yolin(2, В,
cin >> а >> b >> x; /И/ аргументы переменные
yolin f(a, b, a}; cout << у «<< endl;
уЕ
23}; // аргументы-константы
Рассмотрим вычисление плошапи выпуклого многоутольника. заланного
своими координатами. Многоугольник задается координатами п своих вер-
шин, заданных в порядке обхода в опномерных массивахx, у. Лля выпуклого
мнотоугольника вычисляется его плошаль как сумма треугольников, вершина-
ми которых являются точки с номерами: 1.1. #+ 1: Гизменяетсяот 2, a — 1.
Для решения запачи используются функции вычисления расстояния по
двум точкам, заданным координатами x,. у, х., У,. — rast (), и функция вы-
числения площади треугольника по трем сторонам square triangles ():
Функция вычисления расстояния по двум точкам:
double rast (double х1, double yl, double х2, double у2)
return sqrt ((x1l—-x2) % (x1l—x2)+(yl—-y2) * (yl-y2));
Фунеция вычисления площади треугольника по трем сторонам a,b, c!
double square triangles{double a, double b, double с){
double p; // локальная переменная
pH(atbtc) /2;
return sqrt (p* {р-а) *(p-b)*(p-c)};
В главной функции в цикле просматриваются массивы x, у. в которых
записаны коорлинаты вершин. Для текушего индекса точки вычисляет-
ся плошаль текушего треугольника по трем точкам с номерами 0, 2 i/+ 1
после предварительного определения длины сторон через вызовы функции
68
Глава 3. Мринцины разрабонки поднуюграмм. Моднуюсраммы фукыны C++
вычисления расстояния. Плошаль ВЫПУЕЛОГО МНОГОУГОЛЬНиИка ВЫЧИСЛЯЕТСЯ
по рекуррентному соотношению
s=staquare triangles(ta, b, с};
тдеа. В. е — стороны текущего треутгольниея.
Кол файла программной реализации:
#include <iostream>
#include <math.h>
using namespace std;
// прототипы функций
deuble square triangles (double, double , double };
double rast(double, double, double, double};
void main{}{
int: i, mn; double “x, “у,
s,a,b,с;
cout «< " число точек ” << endl;
cin San.
// выделение памяты oon массивы
х2пен double[n]; yonew double[n];
for {int 1=0; i < my; i++)
cin >> x[i] => У;
// ввод массивов
if Fa UME вычисления площади выпуклого многоугольника
for (4-5, бт
тТЕ
if вычисление
расстояний между точками.
(AO) ye Wa, SAL, ЕО:
a=rast(x[9], yO], x[i], у[1]};
b-rast
(x [ij], УЕ, ЕЕ, УР;
c=rast(x[itl)], yiitl], xf]. УГО];
st=square triangles(a, b, с}; // вычисление площади
}
cout<<“”=”<<в<<endl;
1 // конец главной функции
// Описания функций rast и square triangles
double rast (double xl, double yl, double x2, double у2){
return sort ( (xl—-x2)* (x1—x2)+(y1l-y2)*(yl-y2}); }
deuble square triangles(deuble a, double b, double с)1
double р; // локальная переменная
pel(athte)/2; return sqrt (p* (p-a)*(p-b)*(p-c)}; }
Передача простых переменных по адресу или ссылке. Передавать по зна-
чению эффективно только простые переменные. Для Toro чтобы изменить
значение параметра, необхолима информация об адресе параметра в памяти.
Для возврата значений через список формальных параметров в C++ есть две
возможности: передать указатель на аргумент. передать ссылку на аргумент.
Для выполнения перелачи параметра с помошью указателя объявляются пе-
ременные — указатели в списке формальных параметров:
<тип> *“ ‹идентификатор параметра»
3.3. Вызов функции. Передача простых неремемиьсс
69
Для определения значения. хранимого по данному апресу. в (VHKUHH
используется операция разалресации — ч*». В этом случае подпрограмма
работает не с копией аргумента в стеке. а с областью памяти аргумента,
которая не освобождается при завершении подпрограммы. При вызове пол-
программы во временную область памяти стека перелаются адреса аргумен-
тов. соответствующих выходным параметрам. Подпрограмма имеет доступ
к памяти по этим апресам и работает по ним с аргументами: все изменения
формального параметра поппрограммы изменяют значения области памяти
фактического параметра.
Классический пример функции. в которой осуществляется перепача по
указателю. — функция обмена значениями двух переменных:
Void swap (int %х, int *y) {|
|aea
E="u г: чу; “Yy=z; }
В списке формальных параметров функции объявляются указатели:
int “x,
int *y.
Для определения значений. хранимых по данным адресам. в функции
используется операция разыменовывания:
*x, =у.
В вызове функции формальным параметрам (указателям) передаются
адреса аргументов. Адрес обычной переменной в памяти определяется опе-
ратором взятия адреса &. Вызов функции, в которой используется оператор
взятия адреса для передачи адреса простой переменной:
«имя функцим» (&<идентификатор параметра? );
Для рассматриваемого примера в операторе вызова функции передают-
ся адреса аргументов — простых целых переменных А, м: swap (ak, 51):
Ссылки. Передача параметров по ссылке. Ссылка препставляет собой псев-
доним (альтернативное имя) переменной. на которую она ссылается. Запись
<T> = обозначает ссылку на объект типа 7. В то же время ссылку можно
рассматривать как неявный указатель: константный указатель. который всег-
да разыменован. В отличие от указателя, память под ссылку не выделяется,
сама ссылка не является объектом.
Основное назначение ссылки — передача параметров. Ссылки использу-
ются в качестве параметров функции, которая изменяет значение передавае-
мого ей объекта. Имя формального параметра функции становится псевдони-
мом переменной вызывающей функции и позволяет получить к ней доступ.
Оператор ссылки — символ &. Для создания ссылеи в списке формальных
параметров перед илентификатором ставится символ &:
секция формальных параметров»: :-=«имя типа> &‹идентификатор
переменной» [,‹имя типа» & «идентификатор переменной»]
Механизм передачи параметров-ссылок аналогичен передаче по указа-
телку в программный стек копируется не значение передаваемого объекта, а
указатель на него. Ссылка внутри функции выполняет роль этого указателя.
который всегда разыменован.
70
Глава 3. Ириициты разработки подпрюаралмм. Поднрюзраммы функыии C++
В теле функции оператор разадресации не используется. Все изменения
параметра ссылки изменяют значение аргумента. Ссылка становится вто-
рым именем переменной. Инициализация ссылки выполняется при вызове.
Связать ссылку с другой переменной нельзя. При вызове не требуется взятия
адреса аргумента. Предыдущий пример записывается таким образом:
Void swap [int & x, int éy) |
inkЕ:Е-K;ХУ;УЕ;
}
При вызове функции аргументы — илентификаторы простых пелых пе-
ременных А. A:
swap (Е, п);
При передаче по ссылее передаются апреса аргументов (фактических па-
раметров). но это выполняется неявно. Лля ссылок нет алресной арифметики,
ссылка не имеет апреса, невозможно объявить указатель на ссылку, ей нельзя
присвоить нулевой апрес. Ссылки целесообразно использовать при перелаче
не только выходных параметров. но и входных, так как перелать по ссылке
быстрее, чем по значению. При этом копируются в стек только апреса.
В качестве примера перелачи по ссылке можно привести функцию.
возврашающую две простые переменные (два выходных параметра): одну —
через возврашаемое значение. вторую — в списке формальных параметров.
В залаче выпеления пифр целого числа и вычисления количества разрялов два
выхолных параметра и олин входной. Один из выхопных параметров перелает-
ся в списке формальных параметров. используя ссылку int <переменная»,
другой — посредством возврашаемого значения функции. Ниже приводит-
ся реализация функции для решения этой задачи: входной параметр —
целое число п. выходной параметр — сумма цифр. передается по ссылке int
&1, функция возвращает целую переменную — количество разрядов:
int kol razgrf{int о, int &1} {
int Е. Be
7=0,
1=8;
while [n }
{
k=n%10;
в/=10;
j++ 1+=Е;
}
return 1;
}
Кол программы с вызовом функции:
int kol_ razr {int п, int & ); //прототип
void main(void) {
intn,m,Е,9:cin>>m;
s=int kol razr {m, Е); //оператор вызова функции
cout<<Е<*
ae ie ier
// описания функций
3.4. Мередача а функцию аргумениное составных тилов
71
3.4. Передача в функцию аргументов составных типов
В языке С++ механизм передачи в функцию массивов определяется
способом вылеления памяти пол массивы.
Статические массивы. Для статических массивов память выделяется на
этапе компиляции в соответствии с описанием:
«тип» <идентификатор> [«количество»};
По умолчанию функции С++ получают аргументы по значению. фор-
мальные параметры инициализируются значениями этих аргументов. Язык
C++ рассматривает аргумент. являющийся именем массива, как адрес ero
первого элемента.
Функция принимает значение переменной, которое присваивается
формальному параметру. но параметр представляет собой адрес. Технически
это соответствует передаче по значению апреса области копии аргумента.
Функция по этому адресу работает с областью памяти аргумента — исход-
ным массивом.
В функцию передается адрес области. выделенной под массив, незави-
симо OT того. являются ли массивы входными или выходными аргумента-
ми. Количество передается через отдельный параметр пелого типа, так Kak
граница области неизвестна. Поэтому функция имеет доступ к области ap-
гумента и может записывать или изменять значения, содержашиеся в этой
области. Передача апресов в качестве аргумента позволяет не тратить время
и не использовать память на копирование. Фактически происходит перелача
по значению, но передача апреса. а не самого массива.
При передаче массива указываются тип элементов. апрес массива, коли-
чество элементов. В нотации C++ соответствующий формальный параметр
функции — адрес первого элемента в нотации массивов:
«тип» «идентификатор» [];
В нотации C++ в заголовке функции формальные параметры. объяв-
ленные как
< тип» «идентификатор>[] и
< тип> *‹идентификатер>
эквивалентны.
Имя массива — это адрес первого элемента. В задачах. в которых тре-
буется зашита переданного массива от изменений, в заголовке использует-
ся ключевое слово const. В этом случае функция может обрабатывать как
константные. так и неконстантные аргументы. Без использования canst
функция принимает только неконстантные аргументы.
Пример фунЕпий ввода и вывола при передаче адресов статических Mac-
сивов (используется доступ по индексам):
void wwod mas (106 а[],
ink п);
void print mas (const int а[],
int nj;
void main {){
int bf100], с 1100]; int i, ], п;
72
Глава 3. Ириициты разработки nodapozpasa. Подпрюограммы функыии C++
cinза>п:
wwod mas (Ъ, п); print mas (b, п];
wwod mas (с, m); print mas (с, м) ;
i i функция ввода массива, память BHOeIeHa в выЕЕЕакицем функцим
woid мно mas {int a[], int n) |
for {int i =O; itm; itt)
cin >> afi]; }
void print mas(int a[], int п} {//SyHRUMA просмотра массива:
for fint 1=0, 10; i++)
cout << а[1]
<"
me
В главной функции выделяется память под два статических массива.
В функцию ввода передаются имена массивов — апреса начала этих областей,
через целый параметр н по значению передается количество элементов, Ko-
торое не должно превышать размер выделенной памяти.
Передача динамических массивов в функцию. При передаче пинамических
массивов аналогично случаю статических массивов в функцию передается
адрес области памяти. Формальный параметр функции, соответствующий
аргументу массива, объявляется как указатель на тип элементов:
< тип» “чидентификатор» ;
При передаче динамических массивов в отличие от статических прин-
ципиальным является вопрос выделения памяти под массив для входных и
выхолных параметров.
Выделение памяти под массив в вызывающей функции. Если память под
пинамический массив выделяется в вызывающей функции. то механизм пе-
редачи одинаковый как пля входных. так и выходных параметров: массивы
передаются через указатели на начало областей в списке формальных параме-
тров. Если линамический массив — входной аргумент. то область памяти уже
выделена. Передача динамического массива как входного принципиально не
отличается от перелачи статического массива: в функцию передается указа-
тель на начало области (имя массива — это указатель на элемент с нулевым
индексом) и количество элементов.
Пример фунеции определения количества вхождений числа num в мас-
сиве: массив — вхопной параметр в предположении, что он уже созлан:
int found number (int *а, int п, int num) {
int i, К: // Е - счетчик вхождений
for {1=0,
ЕО;
«п; itt}
Е (a[li]=num)
Е++;
return К;
Если линамический массив — выходной аргумент. то функция. получая
апрес выделенной памяти, записывает результаты в эту область аргумента.
Функция ввода элементов евола массива в предположении, что область памяти
создана в ЕЫЗЫБАавницей функции, не отличается от ввода статического массива:
void new masl{int “a, int п) {
For fint 1=0; 1 < п; itt) cin >> ali];
}
3.4. Иередача ¢ функцию аргументов составных питов
73
Создание массива в функции — выделение памяти в функции. Если массив —
выхолной параметр и выделение памяти под него осушествляется в функции,
то рассматриваются следующие полходы к решению. В первом полходе ис-
пользуется возврашаемое функцией значение, во втором — передача апреса
массива через формальные параметры.
Функция в качестве результата может возврашать указатель: возвраша-
емое значение функции определяется как указатель на начало области, вы-
деленной под массив. В теле функции созлается автоматическая локальная
переменная — указатель. выполняется выцеление памяти. оператор return
возвращает указатель на созданную динамическую переменную. Шаблон
описания функции включает в себя локальную переменную — указатель на
тип возврашаемого функцией значения и определение типа результата Е за-
головке функции как указателя:
«тип» *“ «имя функции» («параметры»| |
«тип» “ «идентификатор»!
«тело функции»
return «идентификатор»;
}
Оператор return возврашает переменную (выражение). являющуюся
по типу данных указателем. Пример функции создания массива. в которой
возврашаемое значение функции определяется как указатель. в фунЕЦии
осуществляется выделение памяти. оператором return возврашается ука-
затель на локальную переменную 2, в которой записан адрес начала области
массива: память в вызывающей функции не Еылеляется:
int “пем mas(int п} {
int i, os
Z=new int[n]; // выделение памяти
for {i=0; a. < nz itt) cin >> #[1]; У/ввод
return =z;
}
При вызове подпрограммы возврашаемое значение (адрес области памя-
ти) присваивается указателю. объявленному в вызывающей функции:
то
2 “by п: c= new_mas[10];
Рассмотрим приемы. использующие передачу созданного массива через
списоЕ формальных параметров. Первый прием — использование ссылки на
указатель, в который записывается адрес области памяти:
void new masl( int No & =P,
intп)1
zp=new int[n]; // выделение памяти
for(inti=O;i<myitt)
cin >> =р[1];
При вызове подпрограммы возврашаемое значение (апрес области памя-
TH) присваивается указателю, объявленному в вызывающей функции:
ПО Meaig,с
new masl =, 10):
Второй прием — использование указателя на указатель в Списке фор-
мальных параметров:
74
Глава 3. Нринциты разработкы подпрюзралмм. Поднрюгралммы дункнии С++
void new паза( int wk) в
intn){
int is, “2: g=new int[n];
// выделение памяти
for(1=0:i<npАН
сп.
=[1];
МЕ ЕЕ
соер<<“ eh
Вызов подпрограммы: аргументом является адрес указателя:
it: No сер: me
new паз2( вс, 10);
Подпрограммы функции решения базовых задач. В подпрограмме функции
переписываются в новый массив из исходного! все четные положительные
числа (или удаляются из него отрицательные нечетные). Интерфейс —
входные и выхолные параметры. В списке формальных параметров перела-
ется указатель на исходный массив его элементов. Два выходных параметра:
количество элементов нового массива (передается в списке формальных па-
раметров через ссылку). указатель на адрес области памяти нового массива
{перепается через возвратшаемое значение).
int*® form] (106% a, int п, int ék){
int “tp, *t,
НЕ
t=new int[n]:
for{ 1=0, k=O: i¢nsitt)
1Е(а[1]>0 «& а[1]$2==0)
Е [Е++] =а[1];
tpo=new int[k];
for (i-O;i<ksit+) tp[i]=t[i];
delete [] С;
return tp;
Функция поиска по образцу Определяется индекс вхождения образца, если
нет вхождений возвращается —1:
int poisk.obr {int %а, int п, int k){
int 1=0;
while { i<n && а[1] '=k } itt;
ТЕ {а[1] ==Е)
return i ; else return -1;
В неструктурном алгоритме поиска используется цикл for, break для
выхола из цикла:
int poisk obrl {int *в, int п, int Е ){
int i ,flag—-1;
for (1=0: i¢nzit+t)
if(a[i]J=—k) {
flag=i; break; }
return flag;
}
Пересечение массивов. В Функции создается массив элементов, вхоляших
в оба массива а. В:
105%“ masa perea(int ‘Ya, int n, int *b, int m, int «k) |
TneNoaybpAE
ae6
if (n>=m) yonew int[n];
else yonew int[m];
3.4. Передача в функцию аргуменное состланых munod
И
Рог [1=0, k=0; ene ЗН
j=poisk_obr (6, m, а[1]);
af: Ф 9-0)
У[ЕЕ+]=а [1];
}
tp=new int[k];
for (1=0 ;isk;itt)
ЕР[1]=у[1];
delete [] ч:
return tp;
Функция удаления повторных вхождении. Алгоритм аналогичен преды-
дущему но условие проверки обратное; если нет вхождения. то элемент
добавляется:
int*® del povtf(int %а, int п, int &Е) {
int * y,*tp; int ea
yYonew int[n];
k=0; y[kt++]=a[0]; //записали первый злемент
For {i=l ; i¢n; itt}
if (poisk obr({y,k,a[i])==-1)
//если нет повтора, то добавляется
УГ[Е++1=а[11;
tp=new int[k];
for(i-O;ickzit+) Ер[1]=у[1];
delete [] wr
return tp;
Подпрограммы объединения и дополнения массивов аналогичны. Шаб-
лон файла с разработанными подпрограммами-функциями приводится ниже
и включает в себя директивы подключения файлов, прототипы функций,
кол главной функции, в которой вызываются подпрограммы функции, и
описания подпрограмм:
#include <math.h>
#include <iostream>
using namespace std;
// прототипы функций
void print mas (int “pc,int п);
imt® forml {int* a,int п, int é&k);
int poisk cbr (int Та, .int п, int ЕЕ
int* mass peres({int *a, int п, int “b, int m, int &k);
int*® del povtf{int *a, int п, int &Е);
woid new masl({int *« =,
int nj:
woid new mas2 ( int me
int п);
woid swap (int “х, int ‘*y);
woid main{) {
init Gop eee. мо
int “с, “a, *bh, “bl;
bp;
cin>>n>>m ;
new masl(a, nj); print mas {a,n);
76
Глава 3. Нриициты разработки подпрограмм. Подпрограммы функции С++
new mas2(&b, mj); print mas {b,m);
bi=new int [m];
print mas {b,m);
be-del poevt{a, п, Е); //удаление повторных вкождений
print mas {bp,k};
с-тазз pereg(a, п, b,m, 1); // пересечение массивов a,b
system (“pauseTM);; // задержка илы cin>>i
} // конец главной функции
'/бписания подпрограммы функций
3.5. Передача в функцию многомерных массивов
Многомерный массив рассматривается как одномерный массив. каждый
элемент которого. в свою очерель, представляет собой массив. По имени мас-
сива нельзя опрепелить его размерность и размеры по каждому измерению.
Ina передачи многомерных массивов в качестве параметров применяются два
основных полхола, анапогичных полхолам по перелаче олномерных массивов.
В любом случае в функцию либо передается указатель на уже выделенную па-
мять (пинамическую переменную), либо функция создает ее в процессе своего
выполнения. возвраптая в том и в другом случае указатель на массив указателей.
В первом подходе память под матрицу выделяется в вызывающей, или
главной. функции. В функцию в списке формальных параметров передается
указатель на массив указателей, количество строк и столбцов: принцип пе-
редачи не отличается от передачи входных параметров
Во втором подходе матрица создается в подпрограмме функции и па-
мять под нее выделяется в теле фунеции. В этом случае фунепия возврашает
указатель на выделенную область. THM возврашаемого значения в заголовке
функции опрелеляется Kak указатель на «тип» **. В теле функции объяв-
ляется локальная переменная указатель:
«тип» ““‹имя переменной;
В теле функции оператор return возврашает переменную указатель на
вышеленную область памяти в куче. Вызывающая функция имеет поступ к
этой области памяти.
Можно использовать подход. в котором многомерный массив полменя-
ется опномерным массивом и внутри функции имитируется доступ Е мно-
гомерному массиву.
Механизм передачи матрицы. Если матрица передается в функцию как
вхопной параметр, то матрица уже существует и память под нее выделена.
Аналогично массиву в функцию передается указатель на указатель на область
памяти, количество строк и столбиов. Функция просмотра матрицы:
void printmatr (int ‘*“a, int п, int m)}{
For fint 1=0, i<m; itt) [
For {int 3-0; j < м: j++)
cout << ali] [5] <a
at
cout << endl;
}
3.5. Передача в функиию миогомерлых массивов
Ti
Если память под динамический массив выделяется в вызывающей или
главной функции, то такой же подхол можно использовать и для передачи
выходного параметра. В функцию передается указатель на начало выделенной
области и количество строк и столбцов.
Код функции ввола матрицы, память выделена при вызове:
void wwod matrix {int *%а,
int n, int m)
for {int i =0; 1<0; i++)
for {int j =O; y<my; j++)
cin >> bill:
}
Функция освобождения памяти, выделенной под матрицу:
void del matr [int **a, int n, int No){
For “(int 2-0; 3.< Np РЕ)
delete [] а[1];
delete [] a;
Тип возвращаемого значения — указатель на матрицу. В этом случае матри-
па создается (пол нее выделяется память) в теле функции, функция возвра-
щает указатель на указатель, который используется в вызывающей функции
для доступа в памяти в куче. Например, функция ввода матрицы: входные
параметры — число строк и столбцов я, м1. Выделение памяти осуществляет-
ся в подпрограмме функции, указатель возвращается на массив указателей
на строки (начало области):
int ““newmatrix ( int п, int м) |
int “9,
i, jy; bnew int*[n];
for (1=0; i¢n;itt}
b[i]=new int[m];
for (1 =O; ifn: itt)
Far 4] 292 ym; 7+)
n
Аналогично решаются любые другие залачи, в которых создается новая
матрипа. В частности, функция транспонирования матрицы возвращает ука-
затель на указатель на область транспонированной матрицы:
double “*transp (double %%а, int п, int m}{
Tne: te. aie les
double *“ax;
ах=пем double*([m];
for {1-05 i < mp itt}
ах[1]=пем double[n];
for {a0 ad Seong GK)
forбт<м2
ах [1] [1] =а [1+] [51;
return ах;
78
Глава 3. Нриициты разработки подпрограмм. Подпрограммы функции C++
Базовые задачи обработки матриц. Эти задачи можно подразделить на
два типа' обработка всей матрицы и обработка по строкам или столбцам.
К первому типу относятся запачи: вычисление среднего значения заданных
элементов матрицы, формирование по заланному критерию массива из эле-
ментов матрицы, поиск индексов максимальногоили минимального элемента,
поиск вхождений. преобразование матрицы и т. д.
Описания функций решения задач по обработке всей матрицы. При вычис-
лении срелнего значения четных положительных элементов возврашаемое
значение — среднее. в списке формальных параметров перелается ссылка
&Х {Е — количество запанных элементов}.
Во внешнем цикле используется операция запятая для инициализации
среднего значения 4 и счетчика А:
double sr zhach({ int** a, int п, int mn, int ¢ék){
int i ji double в;
for {i=0, k=O, s=0; 1«п; i++)
far {j-O; Эт 7 ++)
if(a[i] [j]>0
if (К) s/=k;
return 3; }
Функция формирования массива из элементов матрицы выбирает в новый
массив всё нечетные положительные числа, в массив р из временного мас-
сива f переписываются найденные элементы:
int*® form] ( int** a, int п, int nm, int ék) {|
aрee
t=new int[n*m];
for{i=0, Е=0 : din; itt)
for ([1=0; J<m; j ++)
if(a[i][j]>0 && ali) [4]%2)
t(k++)]=alil[5];
tponew int[k];
for (i=O;iek;it+ }
tp[i)]-t[il;
delete [] t;
return tp;
}
Обработка матриц no строкам или столбцам. В этом случае требуется со-
хранение результатов в одномерных массивах. индексы которых изменяются
так же, каки индексы строк или столбцов матрицы. Возможны два полхопа
к решению.
В первом случае входной параметр — матрица — передается в списке
формальных параметров. Итог обработки — динамический массив, в котором
записаны результаты обработки строк. Выделение памяти пля этого массива
выполняется в функции, а сформированный массив результата перелается
через возврашаемое значение функции, тип которого является указателем:
«тип» %чхуказатель>»: Рассмотрим решение на примере поиска индексов,
максимальных в кажлой строке матрицы. В функции создается пинамический
массив индексов столбцов максимальных элементов строк ina, который воз-
вращается фунецией: количество элементов равно числу строе:
33. Передача а функцыю мносомерных
массивов
79
int* ind max{ int** a, int п, int m)
int “ind, j, i;
ind=new int[n];
for(i=0 ; i<n;
;j<m;j++)
‚текущим сравнивается с максимальным в ее
if( alaij(j] > а[1][ 104[1] ] )
ind [i]=j;
Во втором случае обработка каждой строки выполняется подпрограм-
мами-фунЕкциями для одномерных массивов, в которые перелается адрес
текущей i-H строки’:
< илентификатор матрины>[1]|,
* (< идентификатор матрицы > +1},
Рассмотрим функцию сортировки строк матрицы, использующую под-
программу сортировки простым обменом одномерного массива:
void simple sort({int ‘“a,int п)
int i,j,flag;
flag=1;
while (fag) |
flag=0;
Eor{i=0; i<n-1; i++)
if (a[i]>a[i+1]}{
знар(а[1], а[1+1]); dag=i; }
В функции сортировки
строк матрицы в цикле вызывается функция сор-
тировки массива, в которою передаются указатель на строку и количество
элементов — число столбцов:
woidsortstr{int**a,intn,intm){
for(int 1=0 ; i<n; i++) simple _sort(a[i] ,m); }
Аналогично можно реализовать любые подпрограммы, в которых для
кажлой строки вызывается соответствующая функция для обработки олно-
мерного массива: ввод, вывод, нахождение средних в строках, определение
количества заданных ит. д. Вычисляются нулевые элементы в каждой строке
матрицы с использованием функции вычисления 0 в массиве:
int found O(int *a, int n){
int i, k:
for (i=0, k=0; 161: itt)
if(! а[21 ) k++; // или if(a[i]==0) k++;
return К; }
£0
fraea 7 Прияциты разработкы подирограмм_ Подироораемы функымы C++
Функция созлает массив. содержащий количество 0. в кажлой строке:
int* poisk0{ int** a, int п, int m) {
int *ind, 1, i;
ind=new int([n];
for(i=0 ; i<m; i++)
ind[i]= found O(a[i], m) ;
return ind; }
Шаблон главной функции. Фунепию. в которой рассмотрены вызовы
разработанных полпрограмм. в прелположении объявлений переменных. за-
ланных указателями в главной функции. можно прелставитьв виде шаблона:
#include <math.h>
#include <iostream>
using namespace std;
// прототипы вышерассмотренных полпрограмм функций
void printmatr {int **а, int п, in
void wwod_matrix {int **a, int п, int m);
// память выделена пры вызове
void del matr (int **a, int п, int =);
int *"newmAtrix (int no,
int m);
//память выделяется в функции
double sr zhach( int** a, int п, int m, int &Е) ;
int* forml (int** a, int п, int nm, int &Е);
int* ind max{int** a, int п, int m};
void simple sort(int ‘*a,int п);
//функция сортировки простым обменом
void sort_ str (int** а, int п, int m);
int found_Of(int *a, int п);
int* poisk O(int** a, int п, int m);
void swap (int ‘x, int *y};
// главная функция:
void main{) {
int i,j,k,i, n,m ; int ** с, **a, “тоя, ** b,*d:
double =, s1,*ps,*v;
ein>sn>>m; b=new int*[n];
// выделение памяти
for (1=0: i¢nzit++)
b[i]=new int[m];
// вызовы функций ввода и вывода матрицы b, а
wwod matrix (b,n,m);
Printmatr {b, п, m);
//память выделяется в подпрограмме
a= newnatrix (шп, m ); printmatr (а п, м);
„формирование массива по заданному критерию
//передача по ссылке параметра — количество
d=forml (b, п, “My Ep:
s= sr_zhach(b,n,m,k); // вычисление среднего вначения
// формирование массива индексов и вывод значений
inds=ind мах( a, fl; m);
for {1=0; 1<п;1++) cout<< ali] [inds[i]];
3.6. Матричные операции
81
sort str (а, п, mj; // сортировка строк матрицы
//массив из количества нулевых элементов в строке
Я = poisk O(b, п, м);
if сортировка строк матрицы по возрастанию
//ксличества нулевых эленентов
if косвенная сортировка строк матрицы простым обменом:
„обмениваются элементы массива и соответствующие строки
flag=1;
while (flag) {
flag—0;
for (i=0; ai<n-1; itt)
if(d[i]sd[it1]){
swap(& d[i], & d[it1]);
awap( bli], blitl]};
flag-1; }
1 // end while
1// end main
// описания рассыотренных функций (тексты функций)
Вглавной функции осушествляются вызовы разработанных подпрограмм
с иллюстрацией приемов передачи параметров: косвенной сортировки строк
матрицы по возрастанию количества нулевых значений одновременно с сорти-
POBKOH ПОЛУЧенного массива, в котором записаны счетчики нулей в строке.
3.6. Матричные операции
Базовые функции пля выполнения матричных операций — полпрограммы
произвеления, сложения и транспонирования матриц. Интерфейс поппро-
грамм фунеций лля реализации данных операций: на входе лве матрицы. на
выходе — матрица результата операции произведения или сложения. Вход-
ные матрицы передаются в списке формальных параметров: возврашаемое
значение — указатель на указатель, через который перепается адрес области
памяти резульгирующей матрицы.
Прототипы функций. вычисляющтих произведение матриц и возвраша-
юших указатель на созланную результирующую матрицу.
int *“gmlt matrix (int ““a, int %%с, int n, int m, int 1);
double *“~mult matrix [double “*а, int **c, int п, int Ш,
int 1);
ba
Входные параметры: a, с — указатели Ha две матрицы: п, i — число строе
и столбиов а, mi: { — число строк и столбцов с. Вычисление осуществляется в
прелположении, что матрицы согласованы: число строк одной матрицы равно
числу столбцов другой. Произведение матриц — матрица 2: число строк — и.
число столбцов — / Элемент матрицы результата bij опрепеляется как сумма
попарных произведений элементов строки матрицы а на элементы столбца
матрипы с.
82
Глава 3. Ириициты разработки подтрюграмм. Подпрогразлммы функыци C++
Кол функции. вычисляющей произведение матрид:
int *“mult matrix (int *“a, int “*c, int п, int m, int 1]{
int 3, Jr. Е:
int “*b;
b=new int*[n];
// выделение памяти под результат
for (1=0: i < nz: itt) b[i]= new int[1];
// вычисление произведения
for {i=0; i < п; 1++) // по строкам b
for {(1=0; 1 < 1; 1++) // по столбцам b
// по столбцам а и строкам с - вычисление bij
For {k=0 B[i} [3)=0; k < my k++}
г
| Ь [11 [2]+=а[1][К] *< [к] [5];
Feturnh ©;
}
Вычисление квадратичной формы х*АЯхЕ. Здесь р — квадратная матрица
[2*2]:Х — л-мерный вектор. Используются разработанная функция mult _
matrix!) и функпия транспонирования матрицы. Результат перемножения
— скаляр. Фрагмент программной реализации задачи в препположении объ-
явлений переменных. запанных указателями в главной функции:
int ““bo, **we, “ас, **“ wel, 8:
cin2п;
Wo=newmatrix (1, п}; // ввод вектора Х
dc=newmatrix (п, п); // ввод матрицы A
// умножение Х*А, результат-вектор [1*n]
bo-mult matrix (ve, de, 1, п, п};
ясЕ=Екапзр(ус, 1, п); // транспонирование вектора
// квапратичная форма, результат-простая переменная 4
s=mult matrix (be, vet, 1, п, п};
Возведение в степень квадратной матрицы. В теле функции объявляют-
ся локальные переменные: int *%ах , "vo — указатели на матриды,
где ах — вспомогательная матрица, vo — результат На каждом шате цикла
возведения в степень вызывается фунеция перемножения матриц. в EOTO-
рой исходная матрица be умножается на промежуточную резульгирующую
матрицуvo"
int **stepen matrix [int **bec, int п, int эбер){
ime “ew, twee; vig. By oe
// выделение памяти под вспомогательную матрицу
ак=пем int*[n];
For (1=0; i < п; 1++) ак[1]=пем int[n];
// матрица be переписывается BO вспомогательную
//матрицу ах
For {i=0; i < nz itt}
Far Е
song Зе
ax[i][3]=bel4][3];
// цикл возведения в степень
for {k-0; К < step; k++) {
3.7. Указатели на фуикиии
§3
vo=mult matrix(ax, bc, п, п, п};
// для следующего mara матрица результата vo пере-
„/писывается во вспомогательную матрицу ax
For {1=0: 1 < ny itt)
ForЧЕ:J<вatt)
ах [1] [j]=ve[i] [4];
} // конеш цикла
return vec;
ТЕстирование рассмотренных функций осуществляется в прелположении
объявлений переменных описанной выше главной функции. В результате
умножения матрицы ax[n*m] на матрицу be [m*k] вычисляется матрица
de[n*k]. Вызов функция перемножения матриц:
dc=mult matrixfan, bc, п, m, Е]:
printmatr {de, п, kj); /’ вывод результата
3.7. Указатели на функции
Указатель на функцию содержит апрес в сегменте кода. по которому
располагается исполняемый код функции. т. е. апрес передачи управления
при вызове функции. Указатели на функции используются для косвенного
вызова функции. нечерез ее имя, а через обрашение к переменной, храняшей
адрес. а также для передачи имени функции в другую функцию в качестве
параметра. Имя функции является константным указателем на начало копа
функции в оперативной памяти.
Указатель на функции имеет тип «указатель функции, возврашающей
значение запанного типа и имеющей аргументы запанного типа» — сигнагру
функции. Задание типу нового имени осуществляется с помошью ключевого
слова суреЯеЕ, которое используется для переименования типов. Опреде-
ление типа — указатель на фУНЕЦИЮ
typedef <тип> (%имя) (список
типов аргументов);
Например,
typedef double (*fun) (double);
задает указатель с именем fun Ha функцию. возврашаюгтую значение типа
double и аргумент типа double.
г
typedef double (*fun_2) (double, double);
задает указатель с именем fun на функцию, возЕрашающую два значения
типа double и аргумент типа deuble.
Рассмотрим решение задачи табуляции на одномерной и двумерной сет-
ках с использованием указателей на функцию.
84
Глава 3. Нринциты разработкы подпрюзралмм. Поднрюграммы дункнии С++
Табуляция фунЕЦии от одной переменной — вычисление массива
значений функции в узлах равномерной сетки на оси x. Входные параме-
тры: deuble a,deuble В — границы интервала" int п — число узлов:
fun Е — указатель на фунЕЦИЮ от одной переменной типа double (*Еоп)
(double). Возврашается массив значений функции в узлах сетки.
Подпрограмма табуляции функции от одной переменной:
double * tabul 1 (double а, double b, int п, fun Е) {
double h, x, *y¥; int i; yS=new double [п];
// в цикле for используется операция ” , *
for (1=0, h=(b-a})/n, ха; i < my xt=h, y[i]=f£i{x), 1++) ;
return y; }
Табуляция функции от двух переменных — вычисление матрицы 3Ha-
чений фунеции в узлах равномерной сетки на плоскости. Входные параметры:
double a,double 6 — границы интервала по оси xi double с.90061е Я —
границы интервала по оси у; int п — Число узлов, Еап_2 Е — указа-
тель на функцию от двух переменных типа double {+Е un 2} [{double,
deuble). Возврашается матрица значений функции в узлах равномерной
сетки на плоскости.
Подпрограмма табуляции функции от пвух переменных:
double *“tabul 2 {double a, double Ъ, deuble с, double а,
tnt on, Eun
ЕК4
double bse, Бу:
уFaecaeNo,
==пем double “[n]; // выделение памяти
For (1=0; i <n; itt)
z[i]j=new double[n];
// вычисление матрицы значений функции в узлак
// равномерной сетки
For {i=0, hx=(b-aj/n; hy={d-c)/n; 141;
x=ati*hx, itt}
for {1=0: jen; yoorj*hy, =#[41] []1=Е (хх, vir gt+*) :
return z;: }
Kod главной функции. в которой вызываются полпрограммы табуляции:
#include <iostream>
ugzing namespace std;
typedef double {*fun) {double x);
typedef double (*fun2) ( double, double);
// прототипы пользовательских функций
double +11 {double);
double £3 (double, double};
// прототипы функций табуляции
double *tabul lf{double, double, int, fun } ;
double **tabul 2i{double, double, double, double, int п, fun 2);
void main({ ){ // главная Функция
int п; double xn, xk, xl, кд, yl, 94;
// объявление переменных
3.& Задания det самостоятельного выполнения
8
L
a
double **az, “yy;
cin>>n>>xn>>xk;
yotabul 1(xn,xk,n,£1);
cin>>xl>>yl>>=2>>у2
az = tabul2 (xl, yl, хр, y4, в; £3}7 }
// описания пользовательских бункций
double flf{double x) {
return x*sin(x); |
double £3 {double x, double у) [return x*sin (у);}
// описания функций tabul 1, tabul 2
3.8. Задания для самостоятельного выполнения
Задание 1. Дана пелочисленная прямоутольная матрица 4 (n,m), я <= 10,
m <= 15. Разработать слелующие полпрограммы:
* ввОЛа матрицы. вывода матрицы:
* поппрограмму, которая в кажлой строке матрицы находит количество
нулевых элементов и формирует олномерный массив:
* удаления нулевых строк из матрицы путем формирования пругой
матрицы (вхолные параметры — массив, содержащий количество нулевых
элементов в строке, и исходная матрица, выходной параметр — матрица без
нулевых строк).
Задание 2. Лана пелочисленная прямоугольная матрица А {м.н). н <= 10.
Разработать следующие поппротраммы:
* ввода матрицы, вывода матрицы:
* определения количества четных элементов в подматрице выше побоч-
ной диагонали матрицы:
* определения индексов максимального элемента в подматрице. ниже
побочной диагонали:
* преобразования матрицы: строке с максимальным элементом присвоить
количество четных элементов. столбцу — значение максимального элемента.
Задание 3. Дана целочисленная прямоугольная матрица A (м. mt).
Разработать следующие полпротраммы:
* ввода матрицы. вывода матрицы:
* полпрограмму которая в каждой строке матрицы находит количество
нулевых элементов и формирует одномерный массив:
* сортировки строк матрицы по возрастанию количества нулевых
элементов:
* удаления нулевых строк из матрицы.
Задание 4. Дана пелочисленная прямоугольная матрица А(л. м).
Разработать следующие полпрограммы:
* ввОЛа матрицы. вывода матрицы:
* сортировки стро матрицы по возрастанию данного среднего значе-
ния в строках. используя подпрограммы обмена деух простых переменных
и массивов.
Глава 4. ПРЕДСТАВЛЕНИЕ И АЛГОРИТМЫ ОБРАБОТКИ СТРОК
С ЗАВЕРШАЮЩИМ НУЛЕМ
Символьные переменные описываются фунпаментальным типом char.
Строка — это последовательность символов коловой таблицы. Строку можно
рассматривать и обрабатывать и как епиный обЪье,г, и как массив символов.
В языке C++ используются два основных типа прелставления строк:
* нуль-терминальные строки (С-строка), где строка определяется как
символьный массив, который завершается нулевым символом (\ 0) этот
символ с АЗСП-кодом определяет метку конца строки:
* использование объектов класса string, который является частью стан-
дартной библиотеки классов С++.
4.1. Символьные переменные. Нуль-терминальные строки
Базовый тип char определяет целые числа в диапазоне от 0 до 25%.
Эти числа являются колами символов в используемой таблипе кодировки.
В переменной char может храниться один символ из имеюдщтихся в наборе.
Описание символьных переменных:
char «идентификатор»;
Тип определяет размер памяти для переменных этого типа и допусти-
мые операции. Пол символьные переменные отводится память в один байт
Нап символьными переменными попускаются арифметические и логические
операции и операции отношения. Е которых сраёниЕАюЮтТСя КОДЫ СИМЕОЛОЕ
и фунеции из библиотеки обработки символов. Лля использования этих
функций подключается заголовочный файл <ctype.h>. Функции этой би-
блиотеки получают в виде аргумента-символа и оперируют ими как цифрами.
Их основное назначение — проверка принаплежности символа заданным диа-
пазонам: цифр. буЕв. проверки регистра (нижний или верхний). При проверке
реализации функций используется упорядоченность их кодов. Символьные
послеловательности от ‘a’ to'z’ иот “0' до “9? и аналогичные им расположены
в возрастающем порядке при условии. что символ — цифра или буква:
— мгр
г
int Е;
ЕЕ ‘0 geec="*9";
ЕЕЕорЕ"
a" ЕС" 2";
Для перевода символа в цифру из символа вычитается код символа 0:
(ch-"O"). Типы char и +26 могут свободно смептиваться в арифметических
4.1. Символьные переменные. Нуль-терминальные сттрокы
87
выражениях: каждая переменная типа char автоматически преобразуется
Bint. Код символа определяется через приведение типа int (21).
Символьная константа — олин символ, заключенный в апострофы:
char ch="y';. Значением символьной константы является численное
значение этого символа (код во внутреннем машинном наборе). При запи-
си константы, соответствующей символу который не выводигся на печать,
используются два способа.
По первому способу записывается номер символа с предшествующей
обратной косой чертой:
char с2='\13'; char с1='\07,
Во втором способе используется специальная последовательность
СИМВОЛОВ:
char c3="\n"; char c4="\t":
Для чтения и записи символа применяются унаслепованные от языка С
функции getchar() Wputchar (ch) из стандартной библиотеки <stdic.h->.
При вызове функция getchar() извлекает слелующий вводимый сим-
вол и возвращает символ в формате целого в качестве своего значения:
ch = getchar (), функция putchar(ch) выводит символ.
Ввод-вывоп символов может осушествляться операциями помещения в
поток >> и извлечения из потока >> (объекты cin, cout).
Описание и инициализация строк с завершающим нулем. Объявить строку
можно двумя способами. Для описания нединамических строк. т. е. таких
строк. память под которые выделяется на этапе компиляции. объявляется
статический символьный массив с длиной на епиницу больше плины стро-
ки (пля символа “\0"). Такое представление означает, что не накладывается
конкретного ограничения на то. какую плину может иметь строка. Длина
строки ограничена максимальной константой, которая определяется объяв-
лением типа строки:
char «идентификатор» [<дллиназ];
Для представления линамической строи объявляется указатель на char
И БЫДЕЛЯЕеТСЯ Память с ПОМОШЬюЮ процедуры выделения памяти new:
char “‹идентификатор>-пем char [«длиназ];
Примеры:
Char name [100]
- // статический символьный массив
const int len=120;
char st[len];
char name[50); // статический массив
char “str;
str=new char[len]; // динамическая строка
char “stl=new char[70];
Принципиальное отличие описания динамической строки от описания
статической состоит в необходимости выделения памяти в куче под строку.
Кроме TOTO, выделяется память под указатель. Значение этого указателя может
изменяться, Е нему можно применять операции увеличения и уменьшения.
Идентификатор статического массива является константой указателя и не
может изменяться при обработее. К отдельным символам строки можно
Bs
Глава 4. Преденавление м алгоритмы обрабониси cmpox с завегиаловим нулем
обратиться по номеру (индексу) данного символа как к элементам массива
или по значению текущего указателя при просмотре строки. Доступ Е сим-
волу Е ПОЗИЦИИ Г ДЛЯ ДИНАмической строки str:
SEE Et). (Streep. “str, р:
Доступ Е символу в позиции / для строки name:
name[i], *(nameti), “р; тдер — текущий указатель.
Строки можно инициализировать строковыми константами.
Инициализация строки:
сна.
"25,2 83". та", "Age EEE
Нулевой байт (символ (‘0 )) является признаком конца строки если его
не добавить. просто массив символов, а не строка:
char st[]={"l', "2", '3', "4"};
Инициализация строки строковой константой: char st[]="abcd":
в этом случае компилятор сам выделяет память и добавляет символ конца
строки, все лишние элементы инициализируются символом ”\0".
При инициализации динамических строк компилятор вылеляет память
под строковую константу и присваивает указателю алрес начала области
строковой константой, неявно включая нулевой символ:
ГГ
char *str1l=" abaafggag hwjd char *buf=""; // пустая строка
Ввол и вывод строк. Строку можно рассматривать как массив символов
и ввОЛИТЬ ПОСИМвОЛЬНО. В To же время при вволе и выводе в С++ нуль-тер-
минальные строки используются в операциях ввода и выЕолаА и операциях
отношения как простые неструктурированные переменные. Основные спо-
собы ввола и вывола строк слепующие:
* использование операций помещения в поток >> и извлечения из по-
тока >> (объекты cin, cout):
* применение функций считывания строк чесз(з}, puts(s):
* применение функций из унаследованной библиотеки С <stdic.h>:
зсапЕ(}, printi() с использованием формата для строк %3.
Раздепителем при вводе строк с использованием операций извлечения из
потока >>, всапЕЁ (| являются символы пробела, табуляции и новой строки,
поэтому ввод осуществляется до первого символа или знака конца строки.
в конец записывается нуль-символ. Пробел не является разпелителем пля
функций считывания строк gets {<строка>). В этом случае в строку считы-
ваются все символы по знака новой строки. Для чтения последовательности
строк целесообразнее использовать gets (). Общий формат вызова:
cin >> «имя массива символов»;
gets (<MMA массива символов»);
scant (“%3s", «имя массива символов»);
Алгоритмы обработки строк реализуются с учетом проверки достижения
нулевого символа '\0”.т е. при проверке текуший символ сравнивается с
Or.
4.1. Символьные переменные Буль-лчерминальные CAPO
89
Шаблон программы, в которой рассматривается ввод и вывод строк:
#include <icstream>
#include <stdio.h>
#include <string.h>
int const nmax=L50;
using namespace std;
void main{}{
char str[80], s[nmax]; //статические массивы char
char “st,*sp; //указатели на char
ввод вывод
cin >> str;
cout << str << endl;
geta(s);
putea fs]);
st=new char[80];
sp=new char[nmax];
cin >> st;
cout << st << endl;
scant a("ta", sp); printt(ss",sp);
Строка препставляет собой не базовый тип TaHHEIX, а массив, поэтому
присвоить строке значение другой строки нельзя. Для присвоения необходи-
MO OCVIIECIBHTE ПОСИМЕОЛЬНОЕ КОПИРОвание ИЗ ОДНОГО массива Е другой. Над
строками выполняются определенные операции с использованием функций
обработки и преобразования строк. Прототипы и описание функций преоб-
разования строки в число описаны в библиотеке <stdlib.h>:
double atof [const char *s}) — преобразует строку в тип double,
int atoi f(const char “s}) — преобразует строку в тип int,
long atol (const char *s) — преобразует строку в тип long int.
Прототипы и описание основных функций обработки строк объявлены
в заголовочном файле <string.h> ИЛИ <ceatring>:
int strlen {const char “s) — возврашает фактическое количе-
CTBO СИМВОЛОВ, HE БЕЕЛЮЧаАЯ СИМВОЛ ЕОоНЦа Строки:
char “streat (char *sl, const char “s2) — записывает строку
з2 В ЕОНеЦ СТроки =1:
char *strepy (char “sl, const char *в2} — копирует строку
32 В СТрокх =1:
char “вЕкреру {char “sl, const char %82, maxlen} — Ko-
пирует maxlen символов строки 32 вв строку 21:
char * strstr {char “sl, char *32)} — возвращает указатель
на позицию первого вхождения строки =2 в строку si:
int stromp f{econst char “sl, const char ‘*s2) — сравни-
вает лве строки в лексикографическом порядке, не различая прописные и
строчные буквы. Функция возврашает 0, если строки совпадают: возврашает
отрицательное число. если 21 располагается в алфавитном порядке раньше.
чем =2: возврашает положительное число — в противном случае.
90
Глаза 4. Предетавление м алгорильмы обрабонкы стнрюк ¢ запершаглощим нулем
4.2. Основные приемы обработки
строк
Разработаем пользовательские функции посимвольной обработки строк.
аналогичные указанным выше функциям библиотеки. используя в качестве
признака завершения строки нулевой байт (null-terminated string). Строки
можно обрабатывать двумя способами: используя адресную арифметику ука-
зателей или применяя индексную нотацию. Рассмотрим алгоригмы с указате-
IGM char *р, которые на каждом шаге пикла перемешаются к следующему
элементу массива. применяя идиому: *р++;
Распространенный прием — использование двух указателей (или двух
индексов). которые соответствуют областям памяти исходной и результи-
рующей строе. Пикл организуется по символам одной из строк до нулевого
символа. Ha кажлом шаге цикла символ исходной строки копируется в новую
с разадресацией указателей. После этого осушествляются увеличение адреса
на единицу и переход к следующему символу Основное соотношение (21 —
указатель исходной строки; pt — указатель результирующей строки):
while (*pl} *ptt++—*pl+t+;
Определение длины строки. Вхолной параметр функции — строка. возвра-
шается количество символов. Объявляется вспомогательный указатель, пе-
ремешающийся в цикле по массиву ло нулевото байта. при увеличении ко-
торого осушествляется переход к слелующему символу Адрес передается по
значению. поэтому можно не использовать вспомогательный указатель,
а применять формальный параметр. Функция определения ллины строки:
int strlenmy (char *st) {|
int n=0; char “pl; pl=st;
while {{*pl) '= 0) {
pitts nite}
return п; }
Копирование строки. Для этой цели используется алгоритм копирования
символов из источника в приемник. который состоит в следующем. Опре-
деляется длина новой строки, и выделяется под нее память. Ло тех пор пока
исходная строка не закончена, символы источника копируются в область
памяти по указателю результирующей строки. а на каждом шаге указатели
разадресовываются, используя идиому “p++. Для сохранения адреса начала
области памяти новой строки объявляется вспомогательный указатель, ко-
торый в цикле переметается по новому массиву Код функции копирования
строки 22: возвращается указатель на новую строку:
char % strepy my (char *s2) {
char “ptr, “pt;
ptr=new char [strlen(s2)7+1)]; pt=ptr;
while { *s2)
“pttt+S*sZ++;
*рЕ++=“\ 0!
return ptr;
}
42. Основные приемы adpadomnku строк
91
Слияние строк. Функция, осуществляющая слияние (конкатенацию) двух
строк. записывает строку =2 в KOHED строки =1. Входные параметры — указа-
тели настроки char *з1, char *в2, указатель возврашается на новую стро-
ку Описание алгоритма решения: объявляется вспомогательный указатель pt
пля новой строки. опрелеляется ллина новой строки, и выделяется память.
Лвумя циклами символы источников переписываются в результирующую
строку До тех пор пока строка источника (*=1!='\0') не закончена. сим-
волы копируются в область результирующей строки: *ot++=*sl++;
Кол фунеции:
char “ strcat my [char *sl, char. %32}
{
char “ol, “pa,
*otr, “pt;
int len;
pl=sl; pZ=s2; // вспомогательные указатели
len=strlen(sljt+strlen(s2}+1; // длина новой строки
ptr=new char[len]; // выделение памяти по указателю
pt=ptr; // сокраняется значение указателя
// копировать символы строки sl в строку pt, пока
// не найден нулевой ограничитель
while {*“р1) *ptt++=*plt+;
// копировать строку p2 до нулевого символа
while ({(*p2) != 0)
if переместить указатели Pt, pea к следующему CHMBOJY
Ypottt+="pett:
*рЕ++='\0'; // записывается нулевой ограничитель
return ptr;
// возвращается адрес
Вхожление полстроки. Опрелелим число вхождений подстроки 2 в строку у,
позиции каждого вхождения и указатель на оставшуюся часть строки. Реше-
ние осуществляется двумя циклами: внешний цикл проходит по символам
строки, вложенный пикл выполняется, если текушие символы попстроки и
строки совпадают. Для реализации вводятся два вспомогательных указателя
(a, м). Опин указатель — на строку (a), другой на полстроку (Ww), каждый со-
ответственно проходит по апресам своей строки. Во внешнем цикле указатель
строки переходит Ha слелующий символ только в том случае, если не было
вхола во вложенный цикл. На кажпом шаге внешнего цикла вспомогатель-
ный указатель и’ устанавливается на начало подстроки. Сушествует два пути
решения.
Первый путь — текуший символ строки и первый символ подстроки
совпапают. после чего следует вложенный цикл, который выполняется в
случае совпадения символов подстроки и строки при непостижении конца
одной из строк в цикле. Второй путь — BNO в этот цикл He осушествляется
{несовпаление текущего символа строки с началом подстроки), в этом слу-
чае апрес указателя а увеличивается и осуществляется переход к следующему
символу строки.
Если вложенный цикл выполняется, возможны два варианта.
9?
Глаза 4. Npedomamienve м алеоринмы обрабоннкы стук г завершагловцим нулем
1. Либо достигли конца подстроки, т. е. выполняется усповие вхождения
полстроки. в условном операторе опрелеляется позиция вхождения и увели-
чивается счетчии вхождений`
if (*w == '\0')
{
Count -a-¥r count = Ч i? NOSMUMA начала BxAORDECHHMA
подстровы
++;
}
2. Либо выход из этого цикла осуществляется досрочно при несовпаде-
HHH СИМЕОЛОЕ ПОДСТроки и строки. переход к следующему символу строки
не нужен. указатель а не увеличивается. Условный оператор:
if(*w'=*as&n=0)att;
выполняется, если флаг л = 0. В этом случае вложенный DECI не выполняется.
Описание функции вхожления подстроки. Входные аргументы: char 7b —
полстрока, char “т — строка. Функция возврашает указатель на массив, в
этом массиве — позиции вхождения подстроки, выходной аргумент — коли-
чество вхождений передается по ссылке:
int *chet wch [char *b, char *v, int *m)
{
char “a, “wy int count, i, J, в, *temp, “poz и;
j=0; // счетчик вхождений
temp=new int[strlen{v}];
if (strlen{v) > strlen(b)) {|
// устанавливаем вспомогательный указатель на начало строки
а=т;
/’ просматриваем символы строки, пока не конец
while {%а "= "\О'’)
{
w=b;// устанавливаем указатель на начало подстроки
n= 0; и / признак вхождения в цикл и счетчик шагов
/ Е циЕл сравнения символов подстрокы м строки выполняется,
if пока символы COBTaTAnRT MIM не дос
ли конца одной из строк
while [(*a == %м
а(*wLSor
[|*“a=*\or}}{
wit;
att;
att;
|
// альтернатива - использование цикла
ff for {w=b; “wo \!= 7\0’ «sé “a=="“w &а& “a $= "\0";
// мч, att; ntt+])>
// если совпали все символы и достигли конца подстроки
if (*w == '\0'’)
{
count=a-v; ++; // остаток строки с позиции jj
count -= п; // позыция начала вхояления подстроки
}
/;если не вошли в цикл сравнения, тс увеличиваем указатель
if(*“w'=“a££n=0)att;
47 Массивы строк
93
m=j;
poz wonew int[j];
For As=O0; Ш =
r++)
poz _м[1] =Еешр [1];
return poz wy}
}
Шаблон программы обработки строки с использованием функций:
#include <iostream>
int const п=80, nmax=90;
using namespace std;
// прототипы функций функций
int strlenmy [char “st);
int *chet woh [char “ББ, char *v, int *m} ;
char “ streatmy (char “sl, char “s2) ;
char “ strepymy (char “s2) ;
// главная функция
void main [) {
char ‘“razd=" ", *str;
int ‘count, i,k,n;
chase а. “py “5: “wp Sey Sato, када“ г
// тест для поиска подстроки,
„/ например v="asdfgj afgfgj afgjrtt 929”; b="fqj";
if
gets(v); // ввод строки
gets(b); // ввод подстроки
// вызов функции спределения позиций вхождения
// подстроки Ъ в строку т
count-chetwch (Ъ, м, k);
/ / вывод позиций вкождений и остатка строки с этой позиции
for (1=0, p=v; ti es cole
cout << count[i] << ”
“ << endl;
puts( ptcount[i]};
}
// копирование строкы т в новую по указателю str
str=strepy my{w);
puts (str);
// слияние двух строк
w=strcatmy (atr, By}:
pute(" слияние “);
putes (w) ;
}
// Конец главной функции
// описания рассмотренных выше функций
4.3. Массивы строк
Можно описать как статические. так и динамические массивы строк.
1. Описание статического массива. плина которого и длина строки 3a-
даются константой. В этом случае память выделяется на этапе компиляции
и HE меняется в программе?
char «идентификатор>[<количество строк>][<длина строкиЪ]|;
94
Глава 4. Мредетавление и алгоршнмы обработки строк с завелиалощим нулем
Пример:
const int len=100;
const int len strok=80;
char mas stxr[len]
[len strok];
2. Описание массива строк, длина которого задается константой, с дина-
мической строкой. В этом случае объявляется массив указателей на строки:
char “«‹ыдентификатор>»[<«количество cTpoR>]
const int len=100;
char “mas str[len]:
3. Описание линамического массива динамических строк, который
можно рассматривать Kak матрицу символов. Объявляется Kak указатель на
указатель на char:
char ““‹идентификатор>»
Пример: char **шаз str:
Это общий случай создания массива строк. позволяющий создать рва-
ный массив (строки разной ллины), память под строки которого выделяется
при создании. Такое представление массива означает, что HE накладывается
ограничений на длину массива и длину строки. Сначала выделяется память
под массив указателей на строки:
«идентификатор>=пем *“сВак[<длина массива {количество
строк) >];
Выделение памяти под массив указателей mas str!
mas str=new %“сБат[<«длина массива (количество строк)> |];
Память под каждун, пинамическую строку выделяется в ПИЕле на этапе
создания в соответствии с требованиями к памяти:
* {«идентификатор»+1)= new сКак[«“длина текущей строкиЪ];
for({i-0; 1 « (юоличество строк>-1}; i++)
mas str[i]=new *char[<onmua строки>+1];
К отдельным символам /-Й строки можно обратиться по номеру (индексу)
данного символа как Е элементам массива па= вст [1] [1] или по значению
текушего указателя при просмотре строки. Для доступа по указателю к /-му
CHMBONY В Г.И строке возможны три варианта:
“ “таз strt+ijtj);
‘(mas str[i]tj);*p;
где р — текуший указатель данного символа.
Инициализация линамическото массива строк выполняется по правилам
инициализации массивов. Количество строе запано:
char “mas str[3)]-{"abe”, "ЧЕЕд”, "Бусх=Ь"];
С-+- выделяет память под массив из трех строк, добавляя символ конца
строки:
char **mas strd-{"abc", “drigq "“, “byexzbh”};
43 Maecuew строк
95
При таком описании опрепеляется длина массива. вылеляется память под
массив указателей на строки и память под кажлую текушую строку.
Ввол массива строк — рваный динамический массив. Для EBOTa ИСПОЛБ-
зуется буферная строка, размер памяти которой определяется количеством
СИМБОЛОЕ Максимально длинной строки. например БаР=пеи char[/$0]; Ha
каждом шаге цикла с консоли вводится текушая строка в буферную строку’
gets (but), выделяется память под текушую строку в массиве стро и осу-
шествляется копирование в Hee прочитанной строки. Описание функции:
входной аргумент — количество строк. функция возвращает указатель на
динамический массив строе:
char “*mas str {int п) [
char “p, “buf,
+*slow;
cehee Pa Be
buf=new char[80];
slow=new char *[n];
for {i=0: i < np itt+}{
gets (but);
Slow[i]=new char[strlen({buf}+1];
slow[i] =strepy my (but);
}
return slow;
ay
Алгоритм выделения лексем. Рассматривается Ha примере задачи выделе-
ния лексем из предложения (из строки). которые записываются в динами-
ческий массив строк. Алгоригм решения можно разбить на две подзадачи.
Первая — определение количества лексем строки, выделение памяти под
массив указателей на char данного размера: вторая — выделение лексем,
определение их плины. выделение памяти под соответствующую строку Mac-
сива и запись лексемы в эту строку
Основные приемы реализации алгоритма препполагает использование в
качестве разделителя — пробел:
char “str — указатель на строку’
int Е — количество слов’
char *“*sLlow — указатель на массив динамических строк. Алгоритм
выделения лексем основан на посимвольной обработЕке строки и правиле
для определения начала и конца слова.
Условие конца слова: текущий символ — не пробел. следующий символ —
пробел. Условие начала слова: текущий символ — пробел, слелующий сим-
вол — не пробел.
Основной фрагмент реализации алгоригма (Tae р — вспомогательный
указатель. прохолящий по символам строки: пп — индекс начала текушего
слова! len — длина строки):
for (1—0, postr, 16-0, mun-0; + < len-1lp itt; ptt}
// если конец слова
if(р)'=""ge((рм))=")|
“{slowtlc})= new char [i-nnt+iti];
i/ выделение памяти под строку и выделение символов слова
fi
и запись
В элемент
массива
с индексом
le,
96
Глава 4. Мредетавление и алгорилнмы обрабоникы CHIDO с завелмиалющим нулем
// пп-индекс начала слова
for{j7-0, pl=(strtnn}); j < i-nntl; j++)
«{*{slowtle})
+7) —*pl++;
// записывается нулевой ограничитель
* [* {slowtle}+4)="\0';
}
ff end if
// если начало слова, TO увеличивается индекс 1с++
'/ и пп присваивается индекс начала текущего слова
oeр
=FUвОЕ
тм
{
// еслы начало слова
le++;
// le-index
mn=i;
// end for
—
Фрагмент программы вЫлЕЛЕНИЯ символов лексемы:
for {j=0, pl=(strtnn); 7 < 21001; j++)
¥{* (slowtle)+47)=*pl+t+;
Этот фрагмент можно реализовать с использованием библиотечной
функции:
¥{alowtle)—podstr (str, nn, i-nn);
где вызывается функция вылеления подстроки.
Данный фрагмент с использованием индексной записи:
For {i-0, postr, 1-0, mn-0; i < len-l; ан, рее]
// проверка с использованием индексов строки
СЕ(str[i]No©"€&.ste[iti]=*")хх
slow[lc]=new char [i-nnti+i1];
for. (j-0, pl-{stetnn}; 7 < Л: ре)
slow([le] [4]=*pl++;
// записывается нулевой ограничитель
slow[le] [4]='\0'
ЗЕ (str[i] == " " БЕ str[itl
// если начало слова
{Те++; тие;
Функция формирования динамического массива строк — лексем прелложе-
ния. Ниже рассматривается функция. реализующая описанный алгоритм, раз-
делитель — пробел. Функция возврашает указатель на массив динамических
строк, гие строка — текушая выделенная лексема предложения, количество
лексем, входной параметр строка. Прототип функции:
char **kol slowa my (char ‘str, int &k);
где char *str — указатель на строку, int ЕЕ — ЕОЛИЧеСТВО СЛОВ.
char **kol slowa my (char “str, int ЕЁ) {
char “p, “pl, “but;
// вспомогательные указатели
4.3 Массияы строк
97
ff
if
if
if
char **slow;
// указатель на массив динамических строк
int i, 71, ic, 1, len, n=O;
// вспомогательные переменные
// повбавляем пробел в конец строки
if (ste[strlen{str}] !— " "} strc-streatmy {str, “”
len=strlen(str); //длина строки
использованием проверки конца слова:
текущий символ - не пробел, а следующий - пробел
Far 41-0, postr, 19: 2 = Чев-1- «itt, ptt)
Е9py Вgeieee—0%
J++;
*Е=1; // количество слов
slow=new char *[1];
fF
I
выделение памяти под динамическый массив строк (слова!
р-=ЕгЕ;
// вспомогательный указатель на начало массива
while {*str == " "} ВЕ;
пропуск ведущик пробелов и формирование массива
ff (lc - индекс строки):
// в цикле просматриваются символы строки
// Формируется массив строк (le - индекс) с исполь-
ff зеваниеы проверкы условия начала и конца слова:
// если текущий символ - пробел, а следующий —
// не пробел - начало слова, если текущий символ -
// не пробел, а следующий - пробел - конец слова
for [(3—, postr, 16-09, паб; 1 = Jen-l; itt, ptt) {|
I= Be
ie ре
id{
ifff выделениые CHMEBOJIOB слова м вапись в элемент массива
// с индексом lc, по-индекс начала слова
“{slowtle)=new charfi-nnt+i1+1];
for (j7-0, pl=(strtnn); jo < 100; j+})
No {No (slowtlco}+7)—*plt++;
f/f maa slow[1lc] [j]=*pl++;
*{*{slowtlc} +4)="\0';
// записывается нулевой ограничитель
}
/ feonm начало слова, TO увеличиваем индекс,
//nn присваивается индекс начала слова
}
tf) р
aS
твы(pe
Те++; пп: }
/! конец for
return slow;
l
l
“
a
—
—
98
Глава 4. Мредетавление и алеоуминмы обрабоннкы сниюк с завелшиалощим нулем
Рептить запачу выпеления лексем можно. используя библиотечную функ-
ЦИЮ strtok{):
char “strtok (char %81, char %57).
Функция разбивает строку 21 Ha лексемы. разделенные символами в стро-
ке 22. При первом вызове функция получает в качестве аргумента строку 31,
в последуюдщтих вызовах в качестве аргумента передается NULL. При каждом
вызове указатель возврашается на текушую лексему строки 21: в том случае
когда лексем не осталось, возвращается NULL. Исходная строка передается
только при первом вызове этой функции. Последующим вызовам передаются
только разделители, так как эта функция сохраняет исхопную строку и запо-
минает позицию в этой строке межлу вызовами. Код фрагмента программы:
char s[]="abc lkjh
fdasay “;
char “st, “mas str[20); int i, 4-0;
/ / выделение левксем и преобразование строкм 3 в массив строк
st=strtok(s, “ М”);
while (st != NULL) {
mas atr[j]-st; st-strtok(NULL, TM "); Eee}
Функция формирования динамического массива строк излексем предложе-
ния решается с использованием функции strtck |). Дадим описание параме-
тров. Входлныеаргументы: char *strl — строка предложение; char *razd—
строка разделитель лексем: выходные аргументы: int *К — количество лек-
сем: фунеция возвраптает указатель на динамический массив строк:
char **slowa (char “strl, char *razd, int &К){
char “p, *“*slow, “str, ‘but;
int yg os
str=strepy my(strl);
// копирование, для сохранения входной строки
1=0;
pSstrtck(str, razd);
// определяется количество лексем
while ({p) {
Т++; pestrtek (NULL, razd});
}
k=1; /’ формирсвание массива строк
slow=new char *[1]; // выделяем память
1=0;
//индекс массива
postrtok [strl, таза};
while (p) 1
ior
// указатель Ha лексему записывается Е 1-Й злемент
// массива строк
slow[1]=p;
ff puts [ slow[1]) };
1++; БЕВЕЕЕСЕ (NULL, тада}; }
return slow; \
Шаблон главной функции — тестирование функций обработки динами-
ческих массивов строк?
#include <iostream>
#include <string.h> // для вызова функции strtok()
43 Массивы строк
using namespace std;
// прототипы рассмотренных выше функций
char “strepymy [char *s2);
char “streatmy (char “sl, char %=2};
char “*kol slowa my (char “str, int ‘*k}
char “*“slowa (char Y“Ystrl, char “Yrazd, int “k);
char “mas str {int п}
// главная Функция
woid main {) {|
inti=0q.Keb,аа,oe.Е,sale
char str1[100]=" aba afgqgag hwjd dadbsdfd
// указатели на динамические строки
char “p, “*razd—" “.. *buk, ЕЕ;
t
i
i
if указатели на динамические массивы строк
char “slow, *“*slowl ;
char “strat Saba afgqgqag
hwjd dadbsdfd
// тестовая строка
Gn: Е
slow2=mas str {п); // ввод массива строк
for {i=0; i < my; it+}) puts [slow2[i]
// вывод массива
if выпшеление певксем Ma строковой константы
strostrepy (36:4);
slowl-kol slowa my (str, 1};
// вызов Функции выделения лексем
cout << j <= endl;
For Час
=
i++) puts f{slowl [i]
// выделение лексемы из динамической строки
str=new сБаг [250];
gets(str); puts (str);
slowl-kol
slowa_ my(str, 1);
// вызов Функции выделения лексем
For, Че с Sap РЕ)
puts ( slowl [i]
// вызов бункциим выделения лексем
// с использованием strtok();
Slowl-slowa (вк, razd, 671);
puts ("slowaTM); cout << j << endl;
For Ч
2<Е2H)puts[slewfe]
1 // конец главной функции
// описания разработанных выше функций
г.
тг.
100
Глаза 4. Предсмнавление и aueopurita обрабонкы cmpax г завершающим нулем
4.4. Задания для самостоятельного выполнения
Задание 1. Ввести строку и сформировать динамический массив строе
из лексем препложения. В массиве найти самую плинную и самую короткую
строки и поменять их местами.
Залание 2. Ввести послеповательность предложений, разлеленных точкой,
сформировать линамическую матрицу из лексем предложений. Сформиро-
вать динамический массив неповторяющихся лексем (словарь) и определить
частоту вхождений лексем.
Задание 3. Ввести последовательность предложений, разлеленных точкой,
сформировать линамическую матрицу из лексем предложений. Отсортиро-
вать строки по плине.
Задание 4. Ввести строку и сформировать линамический массив строк
из лексем прелложения. Определить число лексем. которые начинаются и
заканчиваются на один и тот же символ.
Глава 5. ПОЛЬЗОВАТЕЛЬСКИЕ ТИПЫ. СТРУКТУРЫ
Основные способы создания пользовательских типов в C++ — образо-
вание структур и классов. Структуры — это простейший вариант классов.
Структуры позволяют хранить элементы разных типов и представляют собой
шаблон нового типа. Дескриптор этого шаблона — пользовательский тип.
На основе шаблона созлаются структурные переменные этого типа. С одной
стороны. использование этих типов вызвано требованием описания объектов
препметной области. с другой — объединением разнотипных или взаимосвя-
занных данных в массивы и матрицы.
5.1. Синтаксис определения структуры.
Структурные переменные
Структура — составной тип данных. состоящий из фиксированного числа
компонент одного или нескольких типов. Тип компоненты любой: числовой,
CHMBONBHEM, ЛОГИЧеский, строковый, массивы, структуры. Подобно массиву
структура используется лля хранения взаимосвязанных панных. но в отличие
от массива эти ланные могут быть разных типов. препставляя собой смесь
ланных. В отличие от массива доступ Е компонентам осуществляется по
имени компоненты, а не по индексу Массив может хранить объекты только
олного типа. Объединение структур в массивы и матрицы позволяет хранить
Е массиве разнотипные панные.
Структура определяет шаблон значения типа. каждая компонента ко-
торого в свою очередь может быть структурой или массивом, но на концах
иерархической структуры могут находиться только компоненты простых
типов. Структуры применяются для описания объектов предметной области,
признаки объектов описываются полями структуры.
Определение типа структуры начинается идентификатором struct и
состоит из заключенного в фиггрные скобки списка описаний, который
заканчивается точкой с запятой. Межлу ними в фиггрные скобки заключен
списоЕ членов. называемых также полями или компонентами, с указанием
идентификаторов полей и типа каждого поля. Поля структуры могут иметь
любой тип. кроме типа этой же структуры. но могут быть указателями на него.
Кажлый член объявления заканчивается точкой с запятой. Описание членов
структур аналогично илентификаторам обычных переменных:
102
Praga 5. Нользованельские mun. Структуры
«Объявление структуры» ::= struct <химя структуры» |
“список членов
структуры»
|
«объявленые лена
структуры»
=
«тип члена структуры {поля)> «идентификатор поля»;
Область определения идентификаторов полей — структура. в которой они
определяются, вне структуры они невилимы (недоступны). Идентификаторы
полей внутри структуры полжны быть различными. Объявление структуры
прелставляет собой схему пля описания ее компонент и не приводит к вы-
делению памяти: оно только определяет шаблон или форму структуры. Се-
мантически поля могут быть как залаваемыми, так и вычисляемыми.
Илентификатор структуры (или дескриптор) — имя нового типа. После
объявления пользовательских типов объявляются переменные или экзем-
пляры типа структуры. Объявление статических структурных переменных:
<идентификатор структуре» <cnMcork идентификаторов переменных»;
При объявлении структурных переменных под них выделяется память в
соответствии с опрелелением шаблона: последовательно под каждую KOMIIO-
ненту в соответствии с типом компоненты. Унарная операция sizeof по-
зволяет вычислить размер структуры: в1=есЕ({<иыя типа структуры»)
и опрелелить размер в байтах пля данного типа.
Переменные составного типа называются нолньыми неременными. Для этих
переменных допускается операция присваивания межлу переменными одного
типа. Приведем примеры объявления пользовательских типов для описания
объектов предметной области, использующих структуры. Описание объекта —
точка на плоскости:
struct point {
float uy, Ур
|
Описания объектов с разнотипными полями — смесь ланных. ОбЪЕЕТ
«товар» с двумя полями! название — строка, пена — число:
struct tovar{
char паше [15]; НсаЕ price; |,
ОбЪеЕТГ «студент» с полями: имя — строка, средний бал — число:
struct stud 1
char паше [20];
int ball; // количество сценок
};
ОБЪЕКТ «ЛИНИЯ» с Числовыми ПОЛЯМИ A, В и структурными переменными —
point tl, 642:
struct line {
float k,b;
point 61,62;
5.1. Cusmaccuc определения структуры. Структурные переменные
103
Объявление структурных переменных:
Point 951,50; tovar tov, tovl; stud “z;
Описание структур, включамиших в качестве полей указатели и массивы. Поля
структуры семантически мотут быть как вводимые, так и вычисляемые. Вво-
лимые поля объезеа" стисание некоторой персоны (например, студента): запа-
ваемые значения и соответственно поля структуры: имя char name[i5] —
статический массив, ampec char “adres — указатель на char; массив опе-
НОЕ int “oc — указатель пля созлания динамического массива; int п —
количество элементов массива с оценками. Вычисляемыеполя объекта: срел-
ний бал Яса= srb, стипендияЯса= stip; Для описания
объекта вводится
пользовательский
тип — структура stud:
struct stud {
char “adres;
char *name[50];
ink mi
int *oc;
float srb; // вычисляемое поле средний бал
float stip; //вычисляемое поле стипедия
Вложенные структуры. Членом структуры может быть другая структура,
которая называется вложенной. Если полем структуры является вложенная
структура. то образуется иерархическая структура с глубиной вложения, рав-
ной 2. Если компонентой вложенной структуры является также структура,
то глубина вложения иерархической структуры увеличивается Ha | ит.д.
На концах иерархической структуры могут находиться только компоненты
простых типов.
Рассмотрим описания объектов: линии, окружности, треугольника. Струк-
тура для описания объекта «линия», которая задается двумя точками. Члены
СТруЕТУРЫ- point 61,62 — вложенные структуры! поля float k,b,r —
вычисляемые поля, семантические значения которых коэффициенты прямой
межлу точками 51, 52 и расстояние между точками:
struct line |
point 61,62;
float г, No, Bb; }:
Структура для описания объекта — «окружность», которая задается сво-
ими центром и рациусом. Член структуры cent — вложенная структура:
struct okr{
point cent; float rad ;
iy
104
raed 5. Пользовательские muna Capyicetype
Иерархическая структура лля описания объекта «треугольник» с впожен-
ными структурными
полями tl, 152, 3:
Объявление структурных переменных:
point Е; line lin; okr kr; ;
stud z, *zp;
Доступ к компонентам структуры. Компонента структуры называется ча-
стичной переменной. Обрашение к ней осуществляется по имени. а не по
номеру Доступ происхолит с использованием точечной нотадии. Обрапте-
ние к значению поля осушествляется с помошью илентификатора полной
переменной и илентификатора поля. разлеленных операцией «точка». Член
определенной структуры может быть указан в выражении с помолтью кон-
струкпии вила:
«Доступ к компоненте структуры» ::=
«частичная переменная» ::=
«идентификатор полной переменной».<«илентийикатор поля»;
Операция «точка» — операция поступа к члену структуры (операция ука-
зания члена структуры). Такзя комбинация называется составным именем.
Составное имя можно использовать везде. Ie допустимо применять тип
поля. Значения полей используются в выражениях анапогично применению
переменных соответствующих типов. Операция указания члена структуры
*«точка» ассоциируется слева направо.
Если = — структурная переменная типа stud, z.name,z.srb z.oc[j] —
обрашения к полям структуры. Доступ в полям вложенных структур опре-
деляется в соответствии с глубиной вложения. Для объекта чоОЕружность»
доступ к полю x точки центра: cokrl.cent.x.
В олнофайловой программе описание пользовательского типа пометщает-
ся перед определением главной функции (внешнее описание). Это описание
глобально для всех функций программы.
В многофайловых проектах описание типа струк выносятся в заголо-
вочные файлы «.h», а исполняемый код (функции) — в файл «источник»
(«*. соо»). в которые пирективой include включаются заголовочные файты.
Указатель на структуру апресует блок памяти пля размещения структур-
ной переменной. не являясь ею’.
«Объявление указателя на структурным тип» :
<= ИМЯ структуры з"еымя указателя»;
Оператор доступа к члену структуры через указатель «->s указывает на
его расположение в памяти:
«указатель на структуру? -> «имя компоненты»
Разапресапия указателя позволяет осуществить доступ через точечную
нотацию:
5. Г. Сыитаксие определения структуры. Структурные nepemeniie
105
* [<Указатель на структуру?).<имыя компоненты»
Например, объявление указателя на тип stud и выделение памяти пол
переменную:
stud “zy; == new stud({)
Описание stud *2; объявляет = указателем на структуру типа stud.
Обращение K полям и их инициализация:
Z->n=2; ИЛИ (*z).n=2;
Приоритеты операций работы со структурами. Знаки +->» И +». наряду с
круглыми скобками для списка аргументов и квапратными скобками для ин-
дексов находятся вверху иерархии старшинства операций. Операции указания
члена структуры ++ +->» ассоциируются слева направо. они старше, чем +*».
Если z — указатель на структуру. то =-><член структуры» обраша-
ется к конкретному члену Выражение ++=->п увеличивает л, а не 5. это
эквивалентно выражению ++ (z->n). Для изменения порядка выполнения
операций используются круглые скобки:
++ {=) ->n увеличивает Z до доступакн.а {2++} ->п увеличивает ; после
поступа:
*=->п извлекает то, на что указывает и;
*=->п ++ — увеличивает и после обработки (так же, как и илиома *p++):
Wett—>n — УБЕЛИЧИБаАЕТ 5 ПОСЛЕ ДОСТУПА Я.
Доступ к члену структуры можно осушествить через точечную нотацию,
разадресуя указатель:
{*<Указатель на структуру») .<MMA компоненты >;
Круглые скобки необходимы. потому что операция указания члена струк-
туры ч.» старше, чем <*ь. КонструЕЦИИ z->n H (*z) .n зЕВИВаАЛентны.
Присваивание значений структурной переменной. Для того чтобы присво-
ить значение полной переменной, необходимо осуществить присваивание
значений полям. Выполняется следующими способами:
* использованием типизированных констант:
* вводом значений полей:
* поэлементным копированием (полная переменная, имеющая значение,
присваивается другой полной переменной; значения переменных соответству-
ющих типов присваиваются частичным переменным (полям переменной).
Доступ к полям структуры выполняется с помошью операций выбора +.»
(точка) при обрашении к полю через имя переменной и *->» при обраше-
нии через указатель.
106
Глава 5. Hoanzosamesscrue mune, Структуры
5.2. Передача структурных переменных в функции.
Массивы структурных переменных
Описание структурного типа «массив», компонентами которого являют-
ся структурные переменные, аналогично описанию массива простых пере-
менных. Для создания динамических массивов используются указатели на
структуру: объявляется указатель на тип структуры и выделяется память с
помошью оператора mew:
<Имя типа структура» “ «идентификатор указателя»;
Выделение памяти под заданное количество элементов массива:
<идентификатор указателя» = new «Имя типа структурах
[«количествоз|;
Пример: stud *“b; b=new stud[n];
Для доступа к элементам массива используется индексная нотация b [1],
где b [i] —полная структурная переменная.
Для доступа к полям компонент этого массива используется индексная
нотация или оператор +—>2:
Ь[1].ваше или (bt+i)-sname; b[i].n, bB[i].oc[j];
залание значений полям:
b[O].n=3; 6Б[0].папе=” 1тапоу”; b[O] .oc[0]=5;
Структуру можно передавать в функцию и возвращать в качестве зна-
чения функции. Для передачи структур в функции используются ссылки
и указатели. Формальные параметры функции, соответствующие входным
или выходным структурным переменным, объявляются как ссылки на тип.
При передаче по ссылке не затрачивается время на копирование. Массивы
передаются по указателям.
Задача анализа положения прямых, точек и окружностей на плоскости
решается с применением пользовательских типов point и окт, описанных
выше, и следующих функций.
1. Функция вычисления расстояния между двумя точками:
float rast (const pointé 61, const points 82}|
return sqrt( (tl.x—t2e-u)4
(tl. x—t2.x)+
(tl. y-ta.y)*(tl.y-t2.y) J;
2. Функция анализа положения пары окружностей: угдиненные, вложенные
окружности пересекаются. Для проверки выполняется анализ условий: если
сумма радиусов больше, чем расстояние между центрами, то окружности
уединенные; если сумма расстояний между центрами и каким-либо радиу-
сом больше другого радиуса, то окружности — вложенные; если расстояние
между центрами равно сумме двух радиусов, то окружности касаются, если
нет, то пересекаются.
5.2. Передача струкнуюрных переменных a Gwin. Массивы сируютурных переменных LOT
int prov [const okré tl, const okre t2}{
// вызов ФфФункциы rast(}
if (rast( ti.cent, tZ.cent) < {tl.rad+tZ.rad))
return 1;
if (rast [ tlwcent, téocent) < ЕТ. хгаа |
rast({tlicent, t2Z.cent) < t2.rad)
return 0;
if (rast(tl.cent, tZ.cent}) > {til.radtt2.rad} )
return -l1:
3. Функицня вычисления матрицы расстояний Torm_x© между н точками,
заданными в динамическом массиве Point *=. Входные параметры: int п —
количество точек; 20112 “z — указатель на динамический массив. Выход-
ной параметр — float **а — динамическая матрица расстояний (память
выделяется в вызывающей функции):
void form г (int п, point “=, float ““a){
at: Bie Sep
for {2=—0; зе ШИ: G44)
for ЕЕ
а; Bt}
ali] [4]= rast (2[i], 2[4]) - // индексная нотация
РИ НЕЕ.ЕВЕ fe besa) eee
Ss
//нотация указателей
4. Функины ввода массива точек (память выделяется в вызывающей
функции}:
void vvod to (int п, point *а){ // ввод
int i; point “p;
For (i=0; i < my itt){
cin >> а[1].к >> afi].x; }
5. Функиыи вывода массива точек:
void vived_to (int п, point ча)|
for {int i=0, Ш ©
р itt)
cout << “so"<< afi] -xca"yo" << a[i]-y«<endl; |
Элемент матрицы положения окружностей, ali] [1] определяет, пере-
секаются вложенные или уединенные НЯиИ /-Я окружности. Память вылеля-
ется пол матрицу в вызывающей функции передается указатель на функцию:
void form_okr {int п, okr *z, int %%а){
for фаре
ох nds С++)
fox {inte j=itls jos п: j++)
// вызов функциы проверки положения
ali] [1]= prov(z[i]), z[4] }; "доступ по индексу
И базар рее ар
//ностация указателей
108
Глава 5. Пользовательские muna. Структуры
В главной функции реализуется решение задачи анализа путем вызова
методов. Исхопные данные — массив окружностей.
Структура одлнофайловой программы:
// описание структур point, okr
struct point {
float x, Wy. Е;
};
struct okr{
point cent;
float rad;
};
// Прототипы функций
float rast(point t1, point 62);
int prov fokr tl, okr t2);
void form okr{int п, okr *2, int **а};
void form rf{int п, ‘point “z, float **a);
// тлавная Функция
void main{ } {
int 1=0, j, Е=0, dl, п, m;
// задание массива структур константами
struct point temp[6] =1{[20, 10}, 1177, 10}, [35, 14}, 4115, 9},
{34, 14|, 145, 14};
cin 3» а:
// описания динамических массивов M выделение памяти
point *masp=new point[n];
okr *mas ok; mas ok=new ое [п];
„выделение памяти под массив окружностей
float “ЕЕ ;
НОЕ ри лот // матрицы положения
for{i=0;i<m-1;1
// чтение из массива констант координат точек
шазр[1]= temp[i];;
// задание массива окружностей
for{i=0;i<n-1;а 1
mas ok[i]. cent=masp[i]); // чтеные из массива точек
cin >> mas ok[i]. rad; // ввод поля
I
„/Вльтернативный вариант ввод по полям
fffoer: (1: 1-Е шв ТЕН)
// cin>>mas_ok[i].cent.x>>
ff mas ok[i].cent.y>> mas ok[i].rad;
// выделение памяти под матрицу расстояний:
rt=new float *[n];
// выделяется область памяти Oo массив указателей
For {i507 dat me Vite
rt[ij=new float([n];
// вызов Функции формирования матрицы расстояний
form r(n, masp, rt);
// выделение памяти под матрицу положения окружностей:
5.2 Передача структурных переменные в умении. Массивы струготурных переменных109
р okr-new int “[о]|;
for (i=0; i < nj it+)
p_okr[i]=new int[n];
// вызов функции определения положения окружностей
form okr { п, mas ok, р okr);
} //конец главной Функции —
Обработка линамических массивоЕ, описывающих смесь данных. Объект,
полями которого являются разнотипные ланные. рассматривается как смесь
ланных. Например. дано описание объекта «персона» (студент). полями
которого являются данные смешанных типов (строки и числовые данные}.
Семантика и тип полей объекта:
char “adres; — указатель на char пля описания адреса:
char name[50]; — массив символов пля описания имени:
int “oc; — массив целых для описания входных данных (например,
массив с оценками):
int п; — количество элементов массива оценок.
float arb; float stip; — числовые поля пля описания вычисляемых
полей (например. средний бал и стипендия).
Описание структуры:
struct satud 1
char “adres;
char паше [15];
int п:
int “ое;
float srb; // вычисляемое поле - средний вал
float stip; /,/ вычисляемое поле - стипендия
Рассмотрим ввод линамических массивов, элементы которых представ-
ляют собой смесь данных. Входной параметр количество элементов массива
OOLEKTOB, вОоЗврашаемое значение — указатель на выделенную память:
stud* wwod mas({ int п }{ // ввод массива структур
ints Ч: ДИ
stud 42,
char в6[80];
if
z=new stud [n]; // выделение памяти под массив объектов
for{ 1=0; 11 ; itt) 1
cin>>st; // ввод строки - промежуточная переменная
= [i].adres=new char [strlen(st)+i];
„выделение памяты под динамическую строку
For{ j=0; j < strlen(st);
j++)
= [i]-adres [j]=st[j]; // посимывольное копирование
Z[i].adres [strlen(st)+1] =’\0';// завершающий '\0”’ символ
с1п>>=[1]. name ; У/ввод статической строки
Cin>>z[i].n;
„/выцеление памяти под поле структуры - указатель на целые
z[i].oc= new int[z[i].n];
fords =O;
ве:
ЭР
110
Глава 5. Пользовательские mun. Скируктуры
// ввод злементов массива - члена структуры
cin>>z[i].oc[j];
Feturnh zy;
Функция просмотра массива структур:
¥Woid print(stud “a, int п) {
for(int i=Ofsi<n ;1++) 1
cout<<" \п Name : “<<ali].name<<
“adres"<< ali]. adres <<endL;
for(int 1=0; j<ali].koloc; j++}
cout<<”
"ecali].oc[j]<<"
я
}
Функция формирования вычисляемых полей структуры: срелний балл,
стипендия:
float srball stud{int no stud *а)
utr
int i, jy, ‘kp floats;
for {1=0, s=O0; 1 < п: itt) {| // цикл по массиву объектов
// вычисление среднего Sanna
for {j=0, ali].srb-0; 7 < а[1]. п: +}
afi]. arb += afi]. ac[q];
ali]. srb /= п; //среднее вначение элемента массива
зв += alil]l.srb; // среднее значение пс массиву структур
}
return 3;
}
Void swap{ stud Ga, stud sb) // обмен значений, пере-
дача по ссылке
{ stud ra Hoa; a=b; b=x; 1
Сортировка массива методом полного перебора по строковому полю. Во
вложенном цикле для сравнения строк вызывается функция stremp |}:
Foid sartball [( stud *а, int п} f{
int i,j, imax;
for {i=0; 1<0-1:1++)
{
imax=i;
for: {j-th ae pet]
if(strcemp(a[i]-name, а[1тах ~.name}>=0)
imax=j;
swap( а[1], aflimax]);
}
5.2 Передача структурных переменные в умении. Массивы струготурных переменных 111
Структура кола программы:
#include <iostream>
using namespace std;
// описание структуры struct stud
// Прототипы функций
// главная функция
woid main [{ }{
struct stud “at;
aneskт.aа:floatgs;
cout<<"PasmMep mMaccuBa”’<<end1;
cin >> п; at=new stud[n];
что stud {n, at);
vived stud (п, at);
s-srball stud (n, at);
sortball (п, at);
vivod stud (п, at);
return 0;
|
#/ описания функции
В главной фунеции осуществляется вылеление памяти пол динамиче-
ский массив ат, вызываются фунЕЦии ввода и вывола массива, вычисления
среднего балла и сортировки.
Исследование функций для отделения корней и локальных экстремумов.
Алгориггы решения используют результаты табуляции функции. Табуляция
фунеции от одной переменной осуществляется в прелположении пользова-
тельского типа point:
struct point {
LaatNo,УЕ1
Вхолные значения: интервал [а, 5], количество узлов п, указатель на функ-
цию double (*fun) (double): выходной параметр — указатель на массив
значений в узлах: функция табуляции:
point *tab {double a, double b, int п, double(*fun) (double)){
int i; double hy; point “Е;
Е=пем point[n];
for {i=0, h={b-aj)/n, t[O].x=a ; i <n гам }{
t [i] -y=fun
(Е [i] -x)};
t[i].xt=h; }
Feturm Cf
}
Для определения интервалов. еключакущих в себя корни функции. ис-
пользуется проверка: произведение значений функции на границах текушего
интервала [xi, х:1+1] должно быть меньше нуля. Это условие вытекает из
того. что на концах интервалов функция меняет знак.
Входные параметры подпрограммы — функции: массив значений ис-
следуемой функции 2, количество узлов и, выхолные — количество искомых
112
Глава 5. Пользовательские mun. Скируктуры
интервалов /н и указатель на массив, элемент которого содержит границы
интервала. Описание функции поиска интервалов нулей:
point* interwal nul(point* =, int п, int ém){
PEFy:Е
Point “dt, “d;
dt=new point [0];
for (i50;. 10 ©
each; Tee)
if q{z2[7]..¥*z2fit1)..y¥°
0)
{
dt [9] sx-=2[i].;
dt [4++].
yo2 [11] -x;
}
d=new point[j];
for: (1-02 aa Ч: СВЕ]
d{ij=dt[ij;
В полпрограммах поиска интервалов формируются массивы. элементы
которых содержат границы интервала. Для хранения границ интервалов ис-
ПОЛЬЗУЕТСЯ ТИП Point! В ПОЛ х записывается начало интервала. Е поле у —
конел интервала.
Для определения интервалов. включающтих в себя локальные экстремумы
функции. используется проверка условия: текушая точка является локаль-
ным экстремумом. если ее значение больше предыдущего и следующего.
В полпрограмме формируется массив. каждый элемент которого равен 1, если
текушая точка является локальным экстремумом и 0. если не является. Вхол-
ные параметры: массив значений исслелуемой функции г. количество узлов Н.
Выходной параметр — указатель на указанный массив. текущий элемент ко-
торого равен 1 или 0. Функция определения интервалов, включающих в себя
локальные максимумы функции:
116“ extrum
шах interwal {point’ zp, int п) {
ane. 5: ae (Yat;
dt=new int [n];
for (i=l, dt[O]=0, dt[n-1]=0; i < n-l; itt)
Е {zp[il.y > РЕ] в“ epliy.y > ЕР].
dt[i]=1;
else dt[i]=0;
В функции определения интервалов, включающих в себя локальные ми-
нимумы. используется условный оператор с проверкой на минимум:
ifЧРру<РЕ
в РЕ]
ру]
5.3. Миозофайловые проекты, структура u связь модулей
113
5.3. Многофайловые проекты, структура и связь модулей
В процессе разработки проекта прелварительно формулируют незави-
симые залачи, кажлая из которых реализуется в виде отдельного файла —
модуля (автономно компилируемой программной единицы). Разработанные
подпрограммы-функции объепиняются по семантическому критерию в би-
блиотеки функций и классов. Библиотека состоит из лвух частей: интерфейса
и файла источника с описаниями функций. Интерфейс в С++ реализуется
в заголовочных файлах (Header Ше)с расптирением -h. Заголовочные файлы
описывают интерфейс набора функций. Интерфейс — обшелоступная часть
библиотеки, содержашая определения пользовательских типов (шаблонов и
описания классов). константи прототипы функций. Файл источника (Sourse
file, или исходный файл) с расширением .cpp. включает в себя реализацию
набора, в которой описываются функции или методы классов.
Модульность в языке C++ поддерживается с помошью директив препро-
цессора. пространств имен. Связь межлу модулями и основной программой
устанавливается с помошью директивы #=1пс1а4е. Заголовочные файлы
подключаются к исходному и другим файлам, которые используют функции
из их реализации с помошью директивы:
af
ae
finclude
<имя>.В
или #11пс1оаЧа «имя»
Если какая-то функция из пругого мопуля вызывается в текушем мо-
пуле. необходимо включить заголовочный файл этого молуля библиотеки.
Директива include копирует содержимое заголовочного файла в файлы. в
которых используются функции из его реализации. Имя заголовочного файла
необязательно должно совпалать с именем исходного файла. Утловые скоб-
ки означают. что заголовочный файл слелует искать в специальных папках,
КАБЫЧЕИ ПОКАЗзывают, что заголовочный файл находится в каталоге проекта.
Необхолимо избегать повторного включения заголовочных файлов в модуль
или главную функцию.
Процесс создания программы из исхолных файлов включает в себя две
стадии: компиляцию (compiling) и связывание, или компоновку (linking).
В процессе компиляции происходит создание объектных файлов (.06]) из
исходных (.срр) и затоловочных (.В). в процессе компоновки создается ис-
полняемый файл (.exe) путем связывания объектных и библиотечных файлов.
Методика создания многофайлового проекта рассматривается на приме-
ре решения предылушей задачи — табуляции функции. В отдельные модули
помешаются описания функций решения задачи табуляции и функции про-
смотра матриц и массивов.
В заголовочный файл с именем tab fun.h помещаются описания типов
и прототипы функций. В модуль решения задачи табуляции, который созда-
ется в файле с именем tab fun.cpp, директивой finclude “tab fun.h”
включаются заголовочный файл и описания функций. В модуль, соответству-
юший главной функции. директивой include включаются заголовочные
файлы, и осушествляются вызовы функций попключаемых молулей.
114
Глава 5. Пользовательские muna. Crpycnyype
5.4. Задания для самостоятельного выполнения
Залание 1. Множество н точек задается своими координатами x,y. Для
описания точки использовать структуру tpoint с двумя полями x, у. Опреде-
лить плошаль всех возможных треугольников между центром координат (0, 0)
и точками множества (межлу кажлой парой точек и центром (0, 0)). Исполь-
зовать структуру treug с полями: три точки типа tpoint и поля — a, р, с. $5 —
стороны и плошаль треутольника.
Разработать следующие полпротраммы функции:
1) ввода и вывода динамического массива,
2) вычисления расстояния между двумя точками и площали треугольника:
3) с использованием разработанной подпрограммы сформировать матри-
пу определения треугольников между центром координат (0. 0) и точками
множества:
4+) вывести матрицу по полям.
Задание 2. Залано множество м прямых в виде у = Ах + РВ. тлекир — ко-
эффипиенты прямой. Для описания прямых использовать структуру — line c
лвумя полями для коэффициентов A, 6: заданы множество н точек (исполь-
зовать структуру с двумя полями tpoint: x, У).
Разработать следующие полпротраммы:
1) ввола и вывода линамического массива:
2) функции определения расстояния точки по прямой: r= КЕ -у ЕВ]!
Г
о
Vlт1;
3) с использованием разработанной функцию и сформировать матрицу,
в элементы которой записываются расстояние от каждой точеи до каждой
прямой:
4) функции определения номера точки и номера прямой с маЕсималь-
ным расстоянием.
Залание 3. Создать массив структур. компонентами которого являются
записи об объекте (клиенг банка, свойства описываются полями! фамилия —
строка: bank — номер банка (пелое): ии — вклад в ланном банке (действи-
тельное число). Залан массив из! номеров банков.
Разработать следующие подпрограммы:
1) ввода и вывода линамического массива клиентов:
2) определения значение общего вклала лля клиентов заданного банка:
3) сортировки массива клиентов по номерам банков:
4) формирования массива из ин фамилий клиентов банков и их числен-
ность. применяя массив структур о ланных клиентов:
5) с использованием разработанных подпрограмм сформировать матрицу
нет, KGET элемент которой содержит вклал клиента в конкретном банке.
Залание 4. Множество п точек запается коорлинатами х. у. Для описа-
ния точки использовать структуру tpoint с полями x, у. Определить все
возможные прямоугольники межлу парами точек. Использовать структуру
пля описания объкта «прямоугольники» с двумя полями типа tocint и вы-
числяемыми полями для определения сторон и плотали.
5.4. Задания оля самостоятельного выполненыя
115
Разработать следующие полпротраммы:
1) ввола и вывода динамического массива точек;
2) формирования массива прямоугольников и вычисления сторон!
3} определение прямоутольника с максимальной плошалью:
4) проверки положения прямоугольников: вложенных, пересекающиеся
или уелиненные, используя функцию проверки положения двух объектов.
Залание 5. Описать структуру пля объекта «треугольник treuc с поля-
ми: три точки типа tocint на, 4, с — стороны.
Разработать следующие подпрограммы:
1) ввола и вывода динамического массива треугольников:
2) проверки положения треугольников (вложенных, пересекающихся или
уединенных) с использованием функции проверки положения двух объектов.
Глава 6. ПРИНЦИПЫ ОБЪЕКТНО
- ОРИЕНТИРОВАННОГО
ПРОГРАММИРОВАНИЯ
Концепция объектно-ориентированного программирования заключает-
ся в том, что приложение будет состоять из объектов. взаимодействующих
между собой посредством сообщений. Каждый объекг имеет имя и свой-
ства, определяющие его существенные характеристики, а также поведение.
определяемое как свойствами. так и событиями. на которые он реагирует.
Приложение, разработанное по принципам ООП. представляет собой не
последовательность операторов. реализующих жесткий алгоритм, а сово-
купность объектов. взаимодействующих межлу собой путем передачи друг
другу сообщений. Объектная концепция в C++ реализуются в виде классов.
6.1. Введение в классы С++. Определение класса
В основе ООП лежат три фундаментальных принципа: инкапсуляция.
наслелование, полиморфизм.
Инкансуляция — объединение данных и методов, работающих с этими
ланными (объединение декларативных и пропелурных элементов, работающих
с этими ланными). В ООП реализован механизм доступа внутри структуры
объекта ввелением уровней инкапсуляции, определяющих области их випимо-
сти и использования. Код и данные объекта могут быть закрытыми (private),
общедоступными, или открытыми (public), зашишенными (protected). Эти
спепификаторы лоступа управляют вилимостью копа и данных.
К закрытой части доступ возможен только из самого объекта. Данные и
кол из секции private поступны внутри класса. Объявления в открытой части
public не имеют ограничений на область випимости и лоступа. Через отЕРЫТУЮ
часть интерфейс осуществляет связь с внешним миром. Класс препставляет
собой модель реального объекта, описывающую его характеристики и пове-
ление в випе панных и функций пля работы с ними, объект — структурную
переменную абстрактного типа данных класса. определяемую пользователем.
Наследование — построение многоуровневой игрархии объектов — по-
томков сушествующих объектов-родителей и наследование полей и методов
от объектов-ролителей с возможностью побавления своих полей и метолов
или переопределения (перекрытия) методов объекта-ролителя.
6.!. Beedenve а классы C++. Определение класса
117
Полиморфизм — способность фунЕЦии обрабатывать панные разных
типов. Поведенческие свойства класса определяются набором вхолящих в
него методов. которые предназначены для решения залачи разными алго-
ритмами. Изменяя алгоритм того или иного метода в объектах-потомках.
можно придавать им отсугствующие у объектов-ролителей свойства. Какой
из методов будет выполняться при обращении к методу с заданным именем,
определяется типом объекта (родитель или потомок) и используемого метода.
Класс используется пля описания моделей реальных объектов препметной
области: поля определяют множество состояний. в которых может находиться
объект метопы — множество аспектов повеления. Объявление класса харак-
теризует пользовательский тип. являющийся молелью объекта предметной
области. который называется объеюмным типом.
В качестве начального определения класс можно рассматривать как со-
ставной тип данных, прелставляющий собой объединение данных и фунЕЦИЙ
пля их обработки. Данные класса называются полями (по аналогии с полями
структуры), а фунЕЦИИ класса — мемюдамы.
Метолпы класса — описанные в классе функции. выполняющие обработку
полей. Поля и методы называются элементами класса (ланные-члены и фунЕ-
пии-члены, компонентные данные, компонентные функции). Все методы
класса имеют доступ KO всем полям своего объекта: все ланные, описанные
в классе, становятся глобальными по отношению к методам класса. Это по-
стигается использованием специального неявного параметра this, который
хранит алпрес области памяти переменной классового типа и неявно переда-
ется метолам при вызове (this — указатель на себя: он всегда указывает на
текуший объект). В функцию передается скрытый параметр this, в ЕОТОром
хранится указатель на вызвавший функцию объект От обычных функций
методы отличаются тем. что при вызове им неявно передается указатель на
объект который их вызвал.
Поскольку ланные и метолы инкапсулированы в опном объекте, все дан-
ные (поля) объекта глобальны по отношению к любым ето методам и могут
в них применяться в виде простого имени. Объявление класса реализуется
КЛЮЧЕВЫМ Словом class, далее запается имя класса, после чето в {} описы-
ваются поля и методы. в конце ставится точка с запятой. Описание класса:
class «имя классового типа»!
“закрытые поля и методы по умолчанию»
[public:]
«описание поступных полей и метолов»
l=
=
i
[private:]
[«<описание закрытых полем и методовЪ|]
(Protected:
[«<опысание доступных полей и методов
[2
<
¥
>
};
Определение класса — это спецификация типа для описания объектов
предметной области. Секции protected, private, public, published
солержат описания полей и метолов и определяют области их видимости.
Спецпификаторы доступа управляют видимостью элементов класса.
Данные с атрибутом доступа public доступны вне класса (в обла-
сти видимости объекта класса) и опрелеляют открытый интерфейс класса.
118
Глаза 6. Приныины ofsecnio-opuenmupordnnioed Hepa
MAL POPs
Интерфейс класса составляют общедоступные члены — данные и функции.
через них реализуется поступ каЕ Е открытым, так и к закрытым членам.
Члены класса, объявленные в секциях protected, — зашишенные. За-
шишенный член ведет себя как открытый по отношению к производному
классу (рассматривается далее) и как закрытый по отношению к остальной
части программы.
Члены класса со спецификатором private доступны только функциям —
членам этого класса. Этот вид доступа принят в классе по умолчанию и pe-
ализует принцип скрытия данных от другого класса или от внешнего воздей-
ствия. Функции, реализующие обработку закрытых данных, могут оставать-
ся открытыми для обеспечения интерфейса с внешней часть программы.
Элементы, объявленные как private, отлеляют интерфейс класса от реали-
зации класса. Данные — члены класса объявляются Tak же, RAK и переменные.
Поля класса моцл быть любото типа (базового и пользовательского}. указа-
телями, массивами. кроме типа этого же класса (но могут быть указателями
на этот класс) и описываются аналогично полям структуры.
Кажлый объект получает уникальный набор полей и общий пля всех
ОбЪЕЕТОВ ДАННОГО класса набор методов. Поскольку панные и методы ин-
капсулированы в одном объекте. все данные (поля) объекта глобальны по
отношению к любым его методам и могут быть в них использованы в виде
простого имени (He составного): идентификаторы формальных параметров
методов не полжны совпадать с илентификаторами полей.
Метол представляет собой вкЛЮЧЕНнНУую В ОбЪект фунЕЦИЮ. ДЛЯ ЕОТОрОЙ
он доступен изнутри. Метод класса — это функция, определение и прототип
которой размешены внутри описания класса. При создании сложных классов
методы выносятся за пределы определения класса. Определение функции
может быть реализовано вне описания класса, но прототип функции всег-
да указывается внугри определения класса. Для чтения и задания закрытых
полей применяются специальные методы (теттеры, сеттеры).
Для определения функции (члена класса вне класса) используется опе-
ратор разрешения области видимости. оператор «: :», обозначаемый двумя
пвоеточиями. В этом случае перед колом функции указывается имя класса,
которому она принадлежит Для разпеления служит оператор разрешения
КОНТЕЕСТа «::». Опрепеление метода вне описания класса:
«тип BOSEPaMAGMOTO значения» «имя класса» of:
«имя функции» («список параметров»)
{<Teno функции»!
Функции — члены класса имеют доступ ко всем членам класса. объяв-
ленным в классе. Функции-члены могут вызывать друт друга напрямую. Это
достигается использованием параметра this, который храниг адрес области
памяти this.
В многофайловых проектах определения классов ЕЫнНОСЯятся в заголовоч-
ные файлы .h, а исполняемый код (определения методов) — в файлы источ-
ника .срр. в которые лирективой включаются заголовочные файлы.
Приведем примеры определений класса как спецификаций типов для
описания объектов предметной области. Описание класса объекта «студент»
{персона). данные — закрытые поля: имя — char паше [80], балл —
6.2 Kavempyniap a decmpyamap класса
119
double bal; , метод — функция вычисления стипендии, открытая функция
set |) обеспечивает доступ к полям пля ввола, функция show) для вывода:
class stud 1
char паше [80];
double bal:
public:
double stipti{
if (bal>4) return 200; else return 100; }
Foid set() { cin >> bal >> name; }
// задание закрытых полей
#019 get_bal{) {return Ба1;}
// возвращение закрытого поля
woid show({) 1
cout << name <<", “ee bal<<endl; }//BHBOn полей
1; И! конец описания класса
Описание класса для объекта точка. поля и метод — открытые:
class рофпЕТ {
public:
double x, ¥, Е;
//метод вычисления расстояния от точки до начала координат
double тазЪ()1
rosqrt (x*xt+y*y); return r}
//заголовок метода - поворот точки на угол da
Point wr Point (double За};
|;
//опысание метода вне класса
Point Рео1пЕ::чка
Point (double da) {
Point Е: t.x=x*cos(daj; t.yey*sin(da); return t; }
6.2. Конструктор и деструктор класса
В состав любого класса входят специальные методы, предназначенные
для создания и уничтожения объектов класса. Для созлания объектов (выде-
ления памяти под них} используется специальный метод конструктор. а для
освобождения памяти — деструктор. Конструктор предназначен для создания
и инициализации данных класса. Он вызывается всякий раз. когда объект
создается в памяти ЭВМ. Объявление объекта принципиально отличается от
объявления переменных базового типа. так как при объявлении экземпляра
переменной автоматически вызывается конструктор для создания экземпляра.
Если конструктор в классе явно не определяется, по умолчанию исполь-
зуется конструктор без аргументов, который создается компилятором С++.
В классе можно определить свой конструктор с параметрами для ини-
пнализации данных. Если в классе объявлен хотя бы один конструктор,
компилятор He будет создавать конструктор по умолчанию. В этом случае
120
Глава @. Прыициты объек NO-O NUNN
PORE NNOAD провалили POR
необходимо определить собственную версию конструктора по умолчанию,
так как будет невозможным создание объекта без иницализации (например,
при созлании динамических массивов). Конструктор определяет необходи-
мый размер памяти под размешение объекта в динамической памяти, осу-
ществляет выделение памяти, помещает адрес этой памяти в экземпляр —
переменную типа «класс», помещает ссылку на объект в переменную this,
которая автоматически объявляется в классе и выполняет инициализацию
полей начальными значениями. Конструкторы могут быть перегружены.
В классе можно объявлять несколько конструкторов для разных случаев ини-
пиализации данных членов.
Деструктор освобождает динамическую память и разрушает объект. Для
объявления конструктора и деструктора вводятся следующие правила, отли-
чающие их от обычных метолов класса:
* имя конструктора и деструктора совпадаетс именем класса (компиля-
тор распознает их в отличие обычных методов):
* конструктор и деструктор не имеют типа возврата;
* основное назначение конструктора — инициализация полей;
* перед именем деструктора ставится знак тильда,
* если не указано ни одного конструктора, он создается компилятором
автоматически:
* если в классе определен конструктор, то необхолимо установить KOH-
структор по умолчанию.
Для инициализации данных членов используются конструкторы с па-
раметрами. Список инициализации передается через список формальных
параметров.
Привелем примеры классов, описывающих объекты геометрии. Класс
Line, описываемый уравнением прямой: у = Ах + No
class Line {
double k,b;
public:
Line() {}// конструктор без параметров
// конструктор с параметрами
Line (double kv, double by) {
k=kv; b=by;
}
woid set({ double kv, double Бу } { k=kv; b=bv ;}
и’ тгеттеры
double get k(j{ return Е; |
deuble get b(j{ return b; |
// методы
deuble point x{){ return -k/b ; }
deuble point yi){
return lo
int prov (const Line & 11){
i { Е==11.Е && b==11.b)
return -1;
else if ({k==1l.k 3
return 0;
else return 1:
}
6.3 Создание экземнляроя класса. Лоснтун к членам класса
121
Описание класса Point осуществляется с объявлением собственного KOH-
структора, список формальных параметров которого используется лля инициа-
лизации полей начальными значениями. Формальными параметрами метолов
являются объекты классов, передаваемые по ссылке: в метоле газЕ_© (Point &)
перелается объект самого класса, в методе класса prov(Line &| — фор-
мальный параметр (объект другого класса):
class Point{
deuble x, у;
public:
double rast{){
return saqrt( s*¥xty*y); |}
double rast t{const Point & t1 {
return
((tl.x-x)* (tl.x-x})+ (ctl.y-y)*. {tl.x-x}):
}
int prov (Line « 1)1{
if (l-get k{)* x+ l.get b(}>y) return -1;
else
— return О;
=
I
// методы доступа к закрытым полям
deuble get х(){ return x; |
deuble get у(){ return у; |}
„/ конструктор без параметров
„/ конструктор с параметрами для инициализации
Point (){x-O; у-0;}
Point (double ху, double ут }{s=xv; yoyv; |
woid get( deuble xv, double уу } { x==v; yeyr 3}
1;
В однофайловых проектах описание класса и методов вне класса разме-
щается перед тестом главной функции main (). В многофайловых проектах
описание классов размешается в заголовочных файлах директивой #include
И ПОДЕЛКИТАается
“« имя файла >.h “.
6.3. Создание экземпляров класса. Доступ к членам класса
Класс является шаблоном (образцом) для созлания переменных типа
класса. Память выделяется тогда. когда определяется объект типа класса.
Конкретная переменная типа «класс» называется экземнляром-неременной или
объектом. Для кажлого экземпляра-переменной конкретного типа резервиру-
ется область памяти для хранения данных, как и для обычных переменных,
в порядке следования полей, а указатели для каждой переменной на точки
вхола в методы — общие. Области вилимости объектных переменных опре-
леляются в соответствии с правилами переменных базовых типов. Описание
переменных объектного типа без использования указателей полобно описа-
нию обычных переменных:
122
Глаза 6. Прыииины ofsencnniio-opuenmuporannoed Hpoepe poral
«имя типа класс»
<идентификаторо;
Примеры объектных переменных: stud v; point t; При создании
этих переменных вызываются конструктор по умолчанию и деструктор лля
их удалении из памяти.
Объявление указателя на класс:
«имя типа класс» * ‹идентификатор указателя>-=
new (‹имя типа класс»};
Для создания нового экземпляра класса в памяти используется оператор
new: stud “vl=new stud{}; Point *tl=new Ро1пе(};
Для удаления применяется оператор delete: delete v; delete +;
Указатель Ha тип класса разрешается инициализировать адресом объекта
того же класса или присваивать ему адрес объекта того же типа:
stud “ptr;
stud =; ptr=ez;
Использование конструктора с параметрами, объявленного в классе Point
для создания объекта класса:
Point “t=new Point (12, 5); Point tl=Point (5,71;
Кажлая переменная объектного типа имеет свои поля и использует ме-
тоды, общие для всех переменных этого типа. Шаблон. в соответствии с Ko-
торым выделяется память пол конкретный объект, попдерживает указатель
на самого себя — указатель this, содержаший адрес области памяти экзем-
пляра объекта. Кажлая функция класса имеет лоступ к указателю this для
вызывающего объекта. Указатель Chis — это скрытый указатель, связанный
с экземпляром класса и указывающий на объект. который вызывает метод с
адресом данного объекта. В состав класса вхолиг указатель на специальную
таблилу с информацией, необходимой для вызова методов, — апреса точек
вхола в методы. При вызове метола объекта выполняется неявный оператор.
СЕЯЗЫБАЮЩИЙ ОбЪеЕТ и его метолы в одну область действия. Отличие метода
класса от обычной функции заключается в неявной перелаче при вызове ме-
тола параметра указателя this, который сопержит апрес области памяти эк-
земпляра объекта: this адресует объект. для которого вызвана фунЕЦИЯ-член.
Доступ K полям и методам объекта. объявленным с атрибутом рос. из-
вне класса осушествляется с использованием операции прямого выбора
{точечной нотации):
«доступ в полю составное имя» Tit
«имя эквемипляра».<имя поляз;
Вызов метола пля обработки данных экземпляра с применением опера-
UHH прямого выбора:
«вызов метода (обращение к методу)» ::=
«имя экземпляра>.<«имя метода>( [«список аргументов>] };
Для класса Point] с полями с атрибутом public доступ с использова-
нием точечной нотации задание значений полям и вызов метода — прямой
доступ:
Pointl 6: Е.х=4;: Е.У=5:
Е.тазЕ{);
6.3. Создание экземпляров класса. Tocniyn к членам класса
123
Доступ К ПОЛЯМ, объявленным как закрытые. осуществляется через вызо-
вы метода для установки значен HH ИЛИ конструкторы. Доступ в классе stud
к закрытым полям (задание значений полям) через открытые методы класса:
stud v; v.set(); v.show();
¥.stip(};
При использовании ука зателей на класс для доступа к членам использу-
ется операция непрямого обращения к члену #—>e!
«доступ к полю» ::= хидентификатор указателя»-><имыя поля»;
<ЕЫЗСЕ Метопа> ::= «идентификатор указателя>-><‹имя метода»
{[<список аргументов>]);
Реализация доступа и вызов метода для указателя на класс Point: за-
дание значений полям с атрибутом public — прямой доступ:
201061 “til; tl=new Point()}:
ti-sx=-4; tl->yH5;
ti->rast(); // вызов метода
Применение оператора доступа к указателю на объект класса эквивалент-
но последовательному выполнению двух операций: применению оператора
разыменования з*» к указателю, чтобы получить адресуемый объект, и по-
следующему применению оператора «точка» для доступа к нужному члену
класса. Выражение tl -> x эквивалентно записи (*t1).x.
Доступ к закрытым полям и методам объекта, объявленным с атрибу-
TOM private, извне класса осуществляется через открытые методы класса
и конструкторы с параметрами. Для класса Point прямой доступ к полям
невозможен, значения залаются через конструктор или метод set |), полу-
чить значения можно через методы double get x{) double get у.
Приведем пример с использованием операции прямого выбора.
Создание экземпляров переменных, использование конструктора с
параметрами:
Point t3—Point(1, 2);
Point dt=Point({5, 5);
Создание экземпляра переменной, использование конструктора без
параметров:
Point t4=Point();
Вызов метода: cout<<t3. rast _t(dt};
При использовании указателей на класс для доступа к членам применя-
ется операция непрямого обращения к члену:
Point *ta-new Point(1, 2); Point *Е4=пем Point(?, 12);
При вызове метода, в который передается ссылка на объект, указатель
разадресовывается:
cout << ЕЗ-ъгавЕ Е (4);
124
Глава 6. Принцины объектие-ориеиироантого проза porns
6.4. Массивы экземпляров класса
Описание струЕТУрного типа массива. компонентами которого являютгся
экземпляры класса. аналогично описанию массива структур. Описание ста-
тического массива объектов:
«имя типа класс» «идентификатор» [<количество»!];
Пример: stud 2[10]; Pointl mt[10], zp[nmax];
ГДЕ птах — константа целого типа.
Для доступа Е полям и методам компонентов этого массива использует-
ся индексная нотапция по аналогии доступа Е полям структуры. являющейся
элементом массива:
<доступ к полю ::=
«имя структурной переменной» [инлекс].‹имя поляЪ;
«поступ к методуз::= «имя структурной переменной» [индекс].
«имя метода >[(список параметров}];
Для класса Pointl с полями с атрибутом public доступ с использова-
нием точечной нотации к!-му элементу массива задание значений полям —
прямой доступ и вызов метода:
1:=0; mt[i].x=4;
met fi) -y=s; mt[i].rast();
Для класса stud с закрытыми полями доступ с применением точечной
нотации к /-му элементу массива:
z[li].set{};
z[i]-stipt{);
Для созлания динамического массива объявляется указатель на тип клас-
са, после чего динамически выпеляется память с использованием оператора
new. Объявление указателя на тип класс:
<MMA типа класс» *<идентификатор указателя? ;
Вылеление памяти пол заданное количество элементов массива:
«идентификатор указателя>=
new «имя типа класс» [<количество»];
Примеры:
stud “wr: и=пем stud[n];:
Point *b=new Point [n];// вызывается пустой конструктор
Для поступа K полям и метолам компонентов этого массива используется
индексная нотация или оператор +—2».
Примеры:
Forfint 1=0; i<n; itt) {
wli].set(); cout << w[fi]- stip{);
}
For{int 1=0; i<ny i++) {|
СТАЕ;
а
(sevis
cout<< ({bti)->rast6{ b[O)) ;
6.5. Вканшение и композиция
125
Решение задач обработки массивов можно показать на примере создания
и обработки массива объектов класса Point (точек) и вычисления матрицы
расстояний между точками.
Функция. вычисляющая матрицу расстояний:
double ** matr rast({ Point * b, int п }{
double “ча, г;
int iy
a=new double “*[n];
for(i=0; ai¢cn; itt)
afij=new double[n];
=O; ai<n-1l; itt)
Far {jHitls je п; j++}
а [11 [j])=6[1) -rast_t(b[j]};
return а;
Структура однофайловой программы:
fineclude <iostream>
#include <math.h>
using namespace std;
//Опысание класса Point над главной функцией:
deuble ** matr rast( Point “ b, int п } ;
// главная функция
void main () 1
ЕЕ. Sd es
Е
double u, у, xl, yl;
cin >> no;
Point *a=new Ро1ос[п];//выделение памяти под массив
for (i-0; ai <¢ mp itt} {
cin>Е>>М:
afi]=Point(x, у);// вызов конструктора класса
for 4{1=0: 1 < п; itt) // просмотр
cout<¢< ali].get x()<<""<< ali].get y(}<<end1;
double ““ha 1;
ba=new double *[n];
for (i=0; i¢n; 1++) ba[li]=new double[n];
// вызов функции вычисления расстояний
Ьа= matr rast{ a, п };
1 // конец главной функции
6.5. Включение и композиция
Включение — взаимоотношения типа «имеет» или «является частью»: один
класс содержит объекты других Елассов.
Композиция — отношения между классами, когда класс (классы) ВЕЛЮЧа-
ется в пругой лас, г. е. является его членом. В этом случае один класс как
часть другого класса вЕЛЮТаАеТСя Е другой класс в качестве объектного поля.
126
Глава 6. Иринцины обаектио-ориеттированиоео нуюераммироеания
Композиция реализуется включением в класс массива экземпляров дру-
TOTO класса или нескольких экземпляров разных классов каЕ членов класса.
которые являются ето объектными полями. Для включения произвольного
числа экземпляров пругого класса служит такой механизм, как наполнение.
Полобное включение объектов в класс реализуется с применением указателей.
Приведем примеры включения и композиции. Еласс Line описывает
объект — линия: члены класса — указатели на объееты` Point *t1,* 62: —
ОбЪЕЕТЫ ТОЧЕИ ТИПа point, описанные выше: поле r — поле, которое опреде-
ляет расстояние между двумя точками, вычисляемое в методе класса.
Описание класса:
class Line
Point *t1,* t2; // включение - классы в классах
double r;
public:
// конструктор по умолчанию
Line{) { Е1=0ем Point()});
t2Z=new FPoint ();
}
// конструктор с параметрами
Line (Point tvl, Point tv2)
{
tl=new Point(tvl.get
х{), tvl.get у(})
Е2=пем Point(tvZ.get_x{), tva2.get_y(})
}
// метод вычисления расстояния между точками
double rast(}{
r=sqrt ((t2-sget_x()-tl->get_s{))*
(t2->get_x(j-tl->get_x{))
t+[{t2d->get_y()-tl-sget_y(})*(te->get_yi{)-tl-> get_y()) };
return Е;
}
// методы поступа к закрытым полям
Point get tl(){return *t1;}
Point get t2(){return *t2;}
// метод вывода значений
void show() {
cout <<" $ =” <<tl-sget x()<< " 7 <<ti-> get у <<
“ba=". << B2aceget®( <2
"aa tét>getур << endl;
}
}; //ROHEL определения класса
Для работы с рассматриваемым произвопным классом необходимо со-
здать его экземпляр. Ниже приволятся структура однофайловой программы
и главная функция, в которой рассматривается создание массива объектов
для класса Line: вызываются метопы, в частности вычисление расстояния
ллины текущих линий (от начала координат до текушей точки).
// списание классов Point, Line
// главная функция
void main () {
6.5. Вкаюченые ы композиция
127
PG: abe. Я
Point “a ;// указатель на класс Point
a=new Point[n]; // выделение памяти под массив точек
// задание массива точек
for {i=0; i = п; itt+}{
cin>K>У;
ali]=Point(x, у); // вызов конструктора
}
Point t0=Point(0,0);
// создание динамического массива экземпляров объекта
Line “b line=new Line[n];
// вызов конструктора по умолчанию
// ввод массива линий, каждая линия
// определяется между точкой +0 м текушей а[1]
For {i-0; a2 < п: itt}
b line[i]=Line(t0, ali]); // вызов конструктора
cout << ” \n просмотр массива точек для линий \п”;
for {int 1=0: i < ms: itt){
Ь line[i].show({);
cout << “п длина линии =” <<(6 Llineti)—>rast
()}<<
endl;
Лля реализации класса Mnogoug, описывающего объект «многоугольник»
используется механизм включения. Закрытыми полями объявляется указа-
тель ст на классовый тип Point и количество вершин п многоугольника.
В конструкторе класса вычисляются вершины многоугольника на основе
данных из массива Ро1пЕ*а. переданного в качестве параметра, и выделя-
ется память пол динамический массив линий mas line. В метопе класса
рек шп () вычисляются ллины сторон многоугольника, которые сохраняются
в линамическом массиве mas line; являющемся открытым полем класса.
В данном классе необходимо реализовать свой деструктор для освобождения
динамической памяти. выделенной под массивы:
class Mnogoug {
int п; // количество вершин
Point “tv; // указатель на классовый тип Point
double p;
public:
Line “mas line; // указатель на массив линий
// конструктор с параметрами
Mnogoug{Point “a, int m) {
n=m; tyv=new Point[m]; mas line-new Line[n];
//выделеные памяти под динамический массив линий
for {int 1=0: i < п: itt)
tv[i]=Point::Point
{a[i].get x(},ali].get у{));
for {int 1=0: i < n-l; itt)
mas line[i]-Line::Line{tv[i], tv[it+l]};
}
128
Maga @. Прывыциты OOBErCTNO-OPUEMIME PORNO NEO
MM LORNA
Mnogoug() {tv=0; mas line =0; n=0;}
// конструктор без параметров
~ Mnogoug {) // деструктор класса
{if {mas dine ве!-0 && tyv!=0 26 п >0)1{
delete [] mas line ; delete [] tv; п=0; п=0; }
// Метод класса вычисления сторон и периметра
deuble per mn{}
{
deuble э=0; int 1;
for [1=1; i < ny; itt){
mas line[i]=Line(tv[i-1l], tw[i]);
st=mas line[i].rast{); }
return в; }
1; #/ end class
Для создания экземпляра класса создается динамический массив точек
типа Point, вводятся значения и передаются при вызове в конструктор клас-
са. после чего вызываются методы вычисления сторон и периметра. Фрагмент
кода главной функции:
cin>>n
Point ‘Ya=new Point[n]; //вызывается пустой конструктор
// заданые массива точек
for (1=0;: i < my itt){
cin+>=>>у;
a[i]=Point::Point
(x, у;
//BHESHBASTCA конструктор с параметрами
}
// Создание экземпляра переменной типа Mnogoug
Mnogoug *bc=new Mnogoug(a,n};
// Вызов метода
cout << ” периметр ” << be —->per по{);
for (1=0; i < my itt){
cout << "длина стороны“ << mas line[i].rast();
6.6. Наследование. Доступ к базовому классу
Механизмы включения и наслелования являются формами отношений
между классами. РГаследованые обобщение) — процесс создания новых клас-
сов — потомков (наслелников, производных классов) из уже существующих
или базовых классов (родителей). При наследовании производный класс
имеет все характеристики класса-родителя и свои собственные. Класс-пото-
MOK, получая все возможности базового класса, лополняет его собственными
возможностями, реализуя переход от общего к частному Производные классы
на каждом уровне наследования являются базовыми для производных классов
следующего уровня. Чем более общим является класс, тем выше его уровень
в дереве наследования.
6.6. Наследование. Лостуя к базовому классу
129
При объявлении произволного класса указывается имя класса-родителя.
Порожденный класс автоматически наследует поля, методы и свойства свое-
го родителя и может пополнять их своими полями. методами и свойствами.
Описание класса-ропителя должно прелшествовать описанию класса-потомка.
Видимость ропительских членов класса наследуется в классах-потомках: раз-
решается в классах-потомках повышать видимость. ее понижение не разре-
шается. Доступ Е полям, описанным в классе-родителе, осуществляется как
к собственным полям. Если имена методов в иерархии классов совпадают,
то говорят. что они перекрываются.
При определении производного класса в первой строке описания ука-
зывается имя произволного класса. знак двоеточие «:». SHAK +-» указывает,
что определяется производный класс. После этого знака указывают один из
спецификаторов вида порождения: public, private, protected, далее
через пробел — имя класса-родителя. После заголовка слепует тело произвол-
ного класса. содержащее объявления ланных и метолов потомка.
Определение производного класса:
class «имя производного класса» :
[public|private|protected] «имя класса родителя» {
г
i:
J
<UJLE HEI производнегоа класса»
Елючевые слова public, private или protected перед именем базово-
го класса-ролителя объявляют доступ к наследуемым членам класса-ролителя
{или базового класса). По умолчанию подразумевается поступ к наследуемым
членам класса private. Методы производных классов имеют доступа Е
членам класса-родителя со спецификатором public или protected, но He
имеют доступ к членам со спецификатором private. Методы производного
класса имеют доступ ко всем своим членам класса с любым спецификатором.
Использование того или иного уровня доступа определяется поставленной
задачей.
Елючевое слово public перед именем класса-ролителя означает общее
наследование. В этом случае все общие (public) элементы базового класса
остаются с таким же уровнем доступа и в производном классе, экземпляр
производного класса имеет поступ к общедоступным членам класса-роди-
теля. Члены базового класса с доступом private становятся недоступными
в порожденном классе. Их использование в порожленном классе возможно
только через фунеции базового класса, которые доступны в порожденном
классе. В результате наследования с уровнем доступа public производный
класс имеет доступ ко всем переменным и функциям класса-родителя, ко-
торые не являются частными (private). Доступ продвигает члены базового
класса с поступами public protected по иерархии порождения: при этом
члены с доступом public будуг доступны экземпляру производного класса,
ас доступом protected моцт использоваться только внутри объекта.
Количество базовых классов может быть любым. что позволяет осу-
шествить множественное наследование. Они указываются друг за другом
через запятую. Каждый класс-родитель должен быть уже описан. или для
130
Глава 6. Мринцины odaecnio-opvenmuporannoed программирования
него должно быть употреблено упрежлающее объявление вида: class
«имя-нласвса».
Приведем пример построения класса на основе двух базовых:
class с: public a, public Ь j{
<члены класса потомка>
Е:
Конструкторы и деструктрукторы производных классов He наследуются
и лолжны быть опрелелены в кажлом производном классе. Произволный
класс может содержать несколько конструкторов. При создании экземпляра
произволного класса сначала вызывается конструктор родителя, а затем
собственный. Деструкторы вызываются в обратном порядке. Экземпляр
произвопного класса содержит все члены базового класса. Лля их инициа-
лизации в производном классе применяются конструкторы с параметрами,
в определении которых через двоеточие после списка аргументов конструк-
тора произволного класса указывается конструктор базового класса со своим
списком аргументов. В списке аргументов конструктора производного класса
объявляются все параметры. необходимые базовому классу:
«расширенная форма объявления конструктора производного
класса»::=
«конструктор производного класса» {список аргументов} :
«имя класса родителя» {список аргументов») {етело кон-
структора» }
При совпадении имен полей и метолов в иерархии классов они перекры-
ваются. В этом случае перекрытое поле предка недоступно полю потомка.
Наследование работает от прелка к потомку: базовому классу и его объектам
недоступны производные классы.
Совместимость типов объектов при наследовании полчиняется опреде-
ленным правилам и осушествляется межлу экземплярами объектов, между
указателями на экземпляры объектов. межлу формальными и фактическими
параметрами. По правилам контроля соответствия типов объекту как ука-
зателю на экземпляр объектного типа может быть присвоен адрес любого
экземпляра любого из дочерних типов. Переменным экземплярам класса
можно присваивать значение этого же типа и любого произволного от него,
те. объекту-ропителю можно присвоить значение объекта-потомка. Произвоп-
ный тип всегда получается более сложным. чем тип предка (родительский).
В качестве примера рассмотрим производный класс для класса ропителя
stud, указанного выше. В произволном классе описываются дополнительные
частные признаки объекта «студент». например наличие работы. Вводятся
новые члены: число рабочих дней и заработная плата за один день. функция
deuble Е{) — метод вычисления зарплаты:
class stud таб : public вЕа9{
int rab, int kol;
public:
if конструктор производного класса
6.6. Наследование. Jocmn к базовому классу
131
stud rab (char *st, double br, int rabv, int kolv)
stud {st,br) {
габ=кабу; kol=kolv;
}
double £() q{return rab*kol; }
1; // конец описания класса
Следующий пример — класс. описывакиций окружность, который можно
определить как потомок класса Point. В классе реализуются методы, фор-
мальными параметрами которых являются объекты. перелаваемые по ссылке:
class Okr : public Point [{
double r;
rad->r;
public:
// конструктор класса с параметрами
Okr (dowble x, double у, double к): Point(x, у}
L=r;}
Skr(){=10;} // конструктор класса Вез параметров
// Метод проверки положения двух окружностей
int prov [const Okr & pt}[{
double +1;
Peint tv;
tv=pt.centr;
if ty set (pt sget xi); pe.get yh);
rl=rast_t(tv);
eal ЗЕ Saree 4
return 1;
else 4£ { Fle рЕ-е
Ll] ‘wile r 4
return 0;
else return -1;
}
/Метод проверки попадания точки в окружность
int prov Е ( Point & pt){
af { х\к < pt.get e()tpt.get y(}) return 1;
else
return 0;
}
7
// Метод класса - вычисление площади
double sq(}{
double PI=3.14159265358979323846;
return (2*PI*rad*rad); }
1; И! конец описания класса
Ниже приводится структура олнофайловой программы созлания массивов
объектов рассмотренных классов:
// описание классов Point, Okr
// главная Функция
void main{) {
int i,n; double x, у; Okr “w; // указатель на класс
wenew Okr(5,68,20); // создание экземпляра
Gkr d=Okr(); // созданые представителя класса
d=Okr (15,18,23); // вызов конструктора с параметрами
// вызовы методов
сое <a м м-та муза:
132
Глава 6. Ириницины обаек тие -орыенииоованиоео нуюоралюмирования
cout << “ d=" << 94.390;
// создание динамического массива объектов класса
cin >> ny ws=new Okr[n];
// в цикле вызывается конструктор с параметрами
// для каждого злемента массива
forfint1=0;i<myitt){
cin>>xX>>У;
wa[ij=Okr :: Okr(x, у, 20};
cout << ” *" e< we[i].saq{};
6.7. Полиморфное общедоступное наследование
Полиморфизм заключается в том, что методы объектов на разных уровнях
иерархии могут иметь одинаковые имена, но различное содержание. Одно
и то He имя метода связывается с различными его реализациями на каждом
уровне иерархии. Это действие применяется всеми объектами иерархии. но
в зависимости от типа объекта иерархии используется по-разному. Способ
поведения определяется типом объекта. который его вызывает. Объекты про-
ИЗвОдНогГО класса могут вызывать метопы предка класса и свои метолы. При
обрашении к методу с заданным именем будет выполняться метод. который
определяется типом объекта (родителем или потомком) и используемым
методом.
Связывание — интерпретация вызова функции во время выполнения,
т.е. определение функции из иерархии классов. Связывание может быть
статическим (на этапе компиляции) и динамическим (на этапе выполнения).
Полиморфное общедоступное наследование реализуется двумя способами:
1) ранним связыванием (простой статический полиморфизм) — полиморф-
ная фунЕция в потомках переопределяет функцию предка. В статическом
полиморфизме функции могут отличаться по сигнатуре (перегрузка):
2) позлним связыванием (сложный динамический полиморфизм) — по-
лиморфная функция в потомках замещает функцию предка.
По первому способу происхолят переопределение методов предка в по-
томке и статическое связывание: какую функцию вызвать. определяется Ha
этапе компиляции. Полиморфизм осуществляется с помошью перегрузки
функций. По второму способу используется механизм виртуальных функций
и связывание осуществляется на этапе выполнения. Полиморфная фунеЦия
в потомках замешает функцию прелка. Выбор виртуального метода опреде-
ляется типом объекта на этапе выполнения программы.
При размешении ссылки или указателя на базовый класс можно ссылаться
на объекты производных типов. В случае простого полиморфизма указателю
на базовый класс можно присвоить адрес потомка. Опнако вызвать через него
метод потомка нельзя. можно вызвать только метол предка независимо от
того, получил ли он адрес препка или потомка.
6.7 Полиморфное ofuedocmpnioe наследование
133
Для того чтобы через указатель на базовый класс можно было вызывать
метолы потомков. применяются механизм виртуальных функций и механизм
позднего связывания в наслеповании.
Виртуальная функция объявляется в базовом классе со CIOBOM virtual
и замешается в производных. реализуя свой конкретный метод. Сигнатура
виртуальных функций полжна совпадать с сигнатурой функции базового
класса — замещение (overriding).
Виртуальная фунеция в базовом классе определяет вил интерфейса.
Выбор функции по указателю в случае объекта с виртуальными функциями
характеризуется типом объекта. на который ссылается указатель, т.е. alpec
объекта, солержашего указатель.
Пример простого полиморфизма — иерархия классов. описывающих
геометрические объекты (прямоугольник, параллелепипел) ит п. В предке
и потомее используются лее полиморфные функции print (}, агеа ():
первая — лля вывода. вторая — для вычисления плошали. Описание класса:
class Rect {
double 4, a, 3;
public:
Rect() 1!
void print() //первый аспект полиморфной функции
{ cout <<” print ВесЕ "<< area()<<endl;
|
Rect (double dl, double h ){
1
{/первый аспект полиморфной функции
double агеа{)1
return
s-d"a;
=
:
В произволном классе полиморфные функции перегружаются:
class
полиморфной функции
а+2*2*а+2*2*4) ;
Если объявить указатель на базовый класс Rect “pe, TO его МОЖНО HC-
пользовать EAE указатель на произволный`
pe=new Вох (23, 17,163)
Олнако в случае простого полиморфизма вызываются методы предка,
но не метолы потомка"
134
Глава 6. Иринцины объектио -ориеннироманного программирования
pecparea(); pe->print();
Для вызова метода потомка можно использовать приведение типа
(Box *)pe->area();
Код главной функции, демонстрация вызовов методов:
void main[) {
Rect “p,pt; pt= Rect (23, 17);
p=new Rect(53, 7);
// вызываются методы класса Rect
pt.area();
pt.print();
р->агеа(); p->print();
Box * РБ, pd;
pb=new Box (23, 17,165);
p2=Box (53, 7,15 );
// вызываются метолы класса потомка Вох
pb->area();
pb->print();
p2.area();
p2.print();
// Meron предка,
используется операция разрешения контекста
p2.Rect::area();
При объявлении в главной функции экземпляров переменных и указа-
телей на базовый класс вызываемый метод не зависит от адреса указателя на
базовый класс, всегда будет вызываться функция базового класса.
6.8. Виртуальные функции и сложный динамический
полиморфизм
Сложный линамический полиморфизм позволяет использовать указатель
на базовый класс для вызова метолов потомка. В динамическом полимор-
физме используются наслелование и механизм виртуальных
функций. кото-
рый имеет вил интерфейса или представляет собой способ вызова фунЕЦиЙ
с помошью указателей. Ссылка или указатель на базовый класс может быть
на объекты произволных типов. Выбор функции по указателю в случае объ-
екта с виртуальными функциями определяется типом объекта. на который
ссылается указатель. Указатель на базовый класс применяется лля вызова
методов потомка.
Сложный линамический полиморфизм и использование виртуальных
функций рассматриваются на примере вычисления определенных интегралов
с заланной точностью разными методами (линейными метолами прямоу-
гольников, методами трапеций и нелинейным метолом парабол или методом
Симпсона). В базовом классе опрелеляется виртуальная функция (заглушка).
определяющая интерфейс. В потомках реализуется вычисление интегралов
OA Ниртуальные функции и сломеный оциамическый полиморфизм
135
различными метолами. В производных класса используется аспект полиморф-
ной функции. Виртуальная функция базового класса замешается конкретным
методом. Конкретный аспект полиморфной функции вызывается через ука-
затель на базовый класс, которому присваивается апрес объекта конкретного
произвопного класса. Ниже описывается модуль с определяемыми классами.
Описание базового класса и указателя на фунЕЦИКи
typedef double
{* f£unj) (double x);
double fl f{deuble x) {return x*sin(x);
}
double +2 (double x) {return -3*xu*cosg {x}; }
class Baza Int{
protected:
float a, b ,eps;fun Е;
public:
vWirtual deuble integral( }{return 0:}:
virtual void print(){ cout <<" Ваха Int ="<< endl;
Вага Int(){}//{a=0; b=0; n=1;}
Baza Int{double av, double bv,int nv, fun £1) {
а=ач; b=by ; n=nv;f=f1;
eps-0.1le-3;}
Ex
Далее рассмотрено описание классов потомков, в которых замещается
виртуальная функция предка. опрелеляющая интерфейс метолов вычисле-
ния интегралов и реализующая конкретный метод вычисления. Описание
класса, в методе которого double integral({ } вычисляется по методу
прямоугольников:
class Int rect: public Baza Int{
public:
double зв;
double integral{ } {
double s,sp,h,y,d;
Jeng int 10 ; n=50;
эр-1; =1;
while { fabs(d)> eps }{
For ({i=0,8s=0, h=(b- а} / п; isms itt)
st={ £f{ath*i} j)*h;
d=s-sp;
f/f погрешность
aps; // текущее в предыдущее
n=n*2;
}
return 3;
}
void print (){
cout <<" Tnt Rect ="<< integral ()<< endl; }
Int rect () {Baza Int (};}
Int rect (double av, double by ,int nv, fun f1):
Baza Intlav,bv ‚пу ,fl){ }
136
Глава 6. [putes obsercntie-o nue nrndpogaioed прерии POR
Описание класса, в методе которого double integral{ } вычисля-
ется по методу трапеций:
class Int trap: public Baza Int{
public:
double в;
double integral ( 7 |
double 3,sp,h,y,d; long int i, Th:
n=50; -sp-F; a=:
while { fabs({d)> eps jf
for (i=l, h=(b- a) /n, s=({ffa)ti(b))*
(6/2); i<n-1;
в+= (ff{ath*i))*h;
d=s-sp;
n=n*2Z;
sp=s; ; // текущее в предыдущее
}return в;
i++)
void print{(){ cout <<" Int trap ="<< integral ()<<
endl; }
Int trap{) {Baza Int); }
Int trap({double av, double by ,int пу, fun £1):
Baza Int(av,bv ,nv ,£1}) {}
Аналогично проектируется класс Int parab: public Baza Integ
в методе которого deuble integral } вычисляется по методу парабол.
Описание класса:
class Int parab: public Baza Integ 1
public:
double в;
double integral( } {
double в, 86,6,
у, 9:
long int i,n ;
n=50;
sp-1; 9=1;
while { fabs({d)> eps jf
for( h=(b-a)/n, з={Е{а)+Е1Ъ}) “(h/3),i=1,x=a;
1<0-1; xwt=h; 1 ++)
if{ i%2) 8+={2* £{x)*(h/3) );
else st=(4* E(x)*(h/3) );
d=s-sp;
sp-3;
n=n*2;
}
return в;
}
woid print(){ cout <<” Int ="<< integral()<< endl; }
Int parab [) {Baza Int(};}
Int parab(deuble av, double by ,int nv, fun £1}:
Baza Int(av,bv ‚пу ,£1){ }
69 Потоковые файлы C++. Создание и обработка
137
Описание главной функции:
main(){
Ваха Integ “pf[6];
int
m=€;
pf[0]=new Int_trap(-1,1, £1);
РЕ[1]=пемы Int parab(-1,1,£1);
pf[Z]=new Int parab(0,1,£1);
pf£[3]=new Int_trap(-1,1, £2);
pf[4]=new Int rect (-1,1,£2);
РЕ[5]=пеы Int_rect (0,1, £2);
or 4(1=0; i< my i++)
cout<<pf[i] ->integral<<
pf [i]->print();
4
0
р
В главной функции объявляется статический массив указателей на ба-
зовый класс. Каждому указателю присваивается адрес одного из потомков.
В цикле по единому интерфейсу вызываются разные аспекты полиморфной
функции, вызов функции определяется адресом указателя.
6.9. Потоковые файлы С++. Создание и обработка
Пол файлом будем понимать поименованный внешний источник ланных:
файл на лиске. который прелставляет собой линейную последовательность
компонент одного типа, заканчивающуюся маркером конца файла. Условно
файл можно изобразить в виде ленты, у которой есть начало, но нет конца: Р—
имя файла = 4117243'.. 4".., где fi — его компонента. Все компоненты имеют
обшее имя файла. Каждая компонента имеет свой номер. Начальный элемент
имеет нулевой номер. С каждым файлом связано понятие текушего указа-
теля или курсора чтения и записи (неявно описанной переменной, которая
указывает на конкретный элемент файла). Действия с файлами проводятся
поэлементно, в них участвует тот элемент, на который «смотрит» указатель,
при чтении (записи) файла перемешающийся на следующий элемент (или
на необходимое количество элементов, если действия выполняются над не-
сколькими компонентами). Файл представляет собой последовательность
считываемых или записываемых данных. Операции могут осуществляться
лишь над компонентами файла.
Система ввода вывода С++ является объектно-ориентированной и ис-
пользует понятие поток, относящееся к любому переносу данных от источ-
ника к приемнику Пол потоком в С++ (stream) понимается логическое
устройство, связанное с физическим устройством ввода и вывода.
Brod данных из файлового потока называется извлечением, вывод в поток —
помещением (включением). Поток определяется как последовательность бай -
тов и не зависит от конкретного устройства, с которым проводится обмен.
По направлению обмена потоки можно разделить на входные (ланные
вводятся в память), выходные (из памяти на диск) и двунаправленные (до-
пускающие как извлечение, так и включение).
138
Глаза 6. Прыныыты obsecneo-opuenmupoeannoss mpospaw мырованыя
По вилу устройств. с которыми работает поток. можно вычелить стан-
лартные и файловые потоки. Станлартные потоки прелназначены лля пере-
дачи данных от клавиатуры
на экран. Файловые потоки служат лля обмена
информацией с файлами на внешних устройствах Выволимая информация
записывается в поток. вволимая информация считывается из потока.
Основные действия нал файлами:
1) файл лолжен быть отЕрыгт: при открытии файла с ним связывается
поток ввола и вывола. 10 начала операции ввола и вынола конкретному
внешнему файлу на лисее ставится в соответствие объект — поток“
2) осушествляется ввол или вывол:
3) файл закрывается.
Классы файловых потоков C++ наслелуют свойства базового класса No05
и являются производными от классов ostream (класс ofstream)_ istream (класс
ifstream), stream (класс fstream). Классы обгеат и Ытеат позволяют созлать
вхолные и выходные потоки. соответственно ввола и вывола. класс ftream
описывает потоки ввода и вывода Для работы с файлами необхолимо пол-
ключить заголовочный файл <fstream>. В нем полелючены загоповочные
файлы <ifstream> — файловый ввол: <ofstream> — файловый вывод.
Используемые классы потоков:
ios — базовый класс потоков-
istream — класс входных потоков:
ostream — класс выходных потоков:
iostream — класс двунаправленных потоков:
iftstream — класс входных файловых потоков:
ofstream — класс выхопных файловых потоков:
fstream — класс лвунаправленных файловых потоков.
Описание классов нахопится в заголовочных файлах Созданные потоки
связываются с файлом на дисее либо с помошью метода классов (фунЕЦИЯя
open()), либо с помошью конструкторов классов. Передаются следующие
аргументы: указатель на char — имя файла и параметр mode, опрепелянипий
способ открытия файла.
Режим открытия файлов залает член ланных перечисляемого типа. ко-
торый определяется следующим образом:
enum mode {арр, binary, in, out, trunc, ate }:;.
Значения флагов и их назначение:
in — открыть для ввола (выбирается по умолчанию Ina ifstream):
out — открыть пля вывода (выбирается по умолчанию для ofstream):
binary — открыть файл в бинарном виде:
aDp — присоединить данные: запись в конец файла:
ate — уничтожить содержимое, если файл существует (выбирается по
умолчанию, если флаг out указан, а флаги ate и app — нет).
Оператор логического «ИЛИ» (|) позволяет составить режим с любым
сочетанием флагов.
Потоки для работы с файлами созпаются как объекты следующих классов:
ofstream — для вывода (записи) данных в файл: ifstream — для ввода
(чтения) данных из файла: fstream — для чтения и для записи данных
6.9 Потоковые файлы C++. Создание и обрабенка
139
{лвунаправленный обмен). Необходимо открыть соответствующий файл и
связать его с файловым потоком. Открытие файла либо с помощью компо-
нентной фунЕции ореп ()}, либо через конструкторы классов означает свя-
зывание потока с конкретным файлом.
Для того чтобы прочитать информацию из текстового файла, требуется
описать переменную типа ifstream. После этого необходимо открыть файл
лля чтения с помошью фунЕЦии ореп(} или, вызывая конструктор класса:
ifstream fl; £1-ifstream("datl.txt”);
f/f или fl.open("datl.txt”);
После открытия файла в режиме чтения из него можно считывать инфор-
мацию аналогично вводу с клавиатуры, только вместо стандартного устройства
БВОЛа cin Необходим ЕХОДНОЙ ПОТОК ОТЕРЫТОГО длЯ Чтения файла: f1>>x;
Два числа в текстовом файле считаются разделенными. если между ними
есть хотя бы один из символов: пробел. табуляция, символ конца строки. Лля
проверки. достигнут или нет конец файла, служит функция eof {}. Функция
возвращает логическое значение true — если достигнут конец файла, если
не достигнут. функция возврашает значение false.
Пикл для чтения текстового числовото файла (а — числовая переменная):
while {121 .е0Е{)) {fl>>a; cout<e< a;} fl.close({};
Функция чтения созданного в блокноте числового файла:
void ReadF( char* name |} {
int k,n,i; ifstream f1;
fi=ifstream(name):
if читается количество компонент
И
Бок (1=0: л1жп; itt) {
Fil>>k;
cout<e< ke” vet
Fl.close();
Альтернатива — использование цикла while:
while {! f£l.eof({)) ТЕТЕ; coute< kee” ae
При чтении (или записи) текуший элемент (TOT, на котором CIOHT указа-
тель файла) считывается в переменную. после этого указатель файла слвига-
ется на следующую компоненту Запись в файл осуществляется аналогично
консольному выводу: вместо стандартного устройства ввода cout необходимо
использовать выходной поток.
Необходимо описать переменную, соответствующую типу потока: вход-
ной, выходной. двунаправленный. Ниже рассматривается общий случай ис-
пользования леунаправленного потока, который позволяет работать с файлом
как при вводе (чтении из файла}, так и при выводе (записи в файл). Объяв-
ление файловой переменной Е — объекта класса fstream: fstream Е;
Файл полжен быть открыт в режиме ios::binary как пвунаправлен-
ный поток:
140
Глаза 6. Принцины baer nie -opuicnmlipordnoee нра Apo
f.cpen(char *< MMA @anMna>,ios:: binary
ios::in
ios::out);
Бинарные данные записываются в определенной логической структуре. они
и считываются из файла в переменную того же структурного типа. Блоки би-
нарных ланных считываются и записываются фунЕЦИЯМи reacd(), write):
istream & read ( {(char*)ébuf, sgizeof(buft) };
Fstream & write { (char*)ébuf, sizeof{buft}) );
Первый параметр методов write () Hread{) — адрес блока записи/
чтения типа символьного указателя char “. Второй параметр указывает.
что бинарные блоки файла имеют постоянный размер байтов независимо
от фактической длины записи. Функция read() считывает sizeof (but)
байт из потока и записывает их буффер (char*) ébut, функция write ||
записывает sizeof (but) байтов, считывая их из буфера. на который ссы-
лается указатель.
Для открытого потока 2. объявленному выше, лля записи объекта but
определяется его апрес, объект приводится к одлнобайтовому типу и записы-
вается sizecf (buf) байт:
E.write{ (char*)é&buf, sizeof({buf) };
Для чтения в объект but из OTEDHITOTO потока Е определяется адрес об-
ласти памяти объекта. который приводится к однобайтовому типу и считы-
вается sizeof (buf) байт:
Е. теаа(
(char*)ébuft, sizeof{but) );
Компоненты файлы — объекты Point:
// Описаные класса Point
roid maint) {
int i,k,j,0,m,1,size,len; point ‘*bl, “Bb, Bp; 0=5;
//массивы констант для стладки
Бог (1—0; isn; itt) {
// запись в файл из массива
bl[i]-xs=xe[i]; b1[i]-y-ye[al];}
out.open( “Filel DAT", ios::out|/ios::binary };
out.write( (сБаг“} Е п, sizeof{n) });
Фот (1=0; 141; i++)
out.write( (char*)& 61[1], sizeof{bl[i]} }:
out.close({};
cout<¢<endl<< "“Filel.DA “<<endl;
// чтение из файла в массив
in.open( “Filel.DAT”,
ios::rinlios::binary |:
in.,read{ [(char*})& п, sizeot{t п });
b=new point[n];
for{i=0; i<n; it+){
6.9 Нотоковые файлы C++. Создание и ofpadonca
141
in.read({ {char*}«e b[i], sizeof( p) );
cout<< “ ye "<<”“
"ecb [i] .y<<endl1;
}
in,.close({);
Функция просмотра файла. в которую передается имя файла st:
void file bin print(char “st){
int len,i,j,m; point p;
fstream f;
F.open(st, ios::binary | iog::in | ios::out);
while (! £.eo£f{}) {
F.read({ {char*} = р, sizeof( p });
cout<< p.xc<"
Vee p.y<cend! ; }
f.close();
Возможность доступа к байту с нужным порядковым номером реализует-
ся с помошью двух пар функций: зеека{(}-взеехр{(} HWtellgd-tellp().
Нумерация начинается с нуля. Функции зеека (num) и seekp (num) пе-
ремешают файловый указатель на байт файла с номером num, а функции
=е119(} Htellp({) возврашают номер текушего байта. на котором распо-
ложен файловый указатель.
Перегруженный вариант функций seekgq({) И зееКр {) имеет два
параметра:
1) относительное смешение от базовой позиции (может быть как поло-
жительным, так и отрицательным):
2) определение базовой позиции, которое (может принимать три значе-
ния: ios::beg (базовая позиция совпадает с началом файла). ies: :end
{базовая позиция совпалает с концом файла) и ics::cur (базовая позиция
совпадает с текушей позипией файловото указателя). Функция, лемонстри-
рующая использование прямото поступа:
woid file bin mod(char *st){
fstream Е;
int len,i,;j,;M,Size; point р:
F.open(st, icas::binary | 109::1а | ios::out};
E.seekg(0, dos::end); //
указатель на конец файла
len-f.tellg{); з1хе-вакеоЕ (point);
m=len/sizeot(peint); // количество компонент
Е. зееКа (0, 108::6е49|; // указатель в начало файла
Рок (1=0; i<m; itt){
seekg(size*i, ios::beq J);
read( {char} р, sizeoit pl oh;
.xtol; op.yt-10;
.geekp(size*i, ios::beg };
-write((char *) &p, size);
B
h
P
a
O
F
h
P
h
a
р
O
o
t
o
т
В функции молифицируется текушая компонента файла: компонента
читается, значения полей изменяются, указатель возврашается в препыпушую
позицию. и записывается новое значение.
142
Глава 6. Приинины обзектио-ориеиитированного прозраммнрования
6.10. Абстрактные типы данных и их представление
с помощью классов
Основные структуры данных, реализующие абстрактные типы — линей-
ные списки. деревья и сети. Списки описывают линейные структуры. деревья
и сети — нелинейные структуры данных.
Линейными списками называются пинамические структуры данных,
которые представляют собой совокупность линейно связанных однородных
элементов. Каждый элемент списка посредством указателей связывается с
пругими элементами. Разрешается побавлять и удалять элементы. Существуют
однонаправленный и двунаправленный списки.
В олнонаправленном списке каждый элемент содержит поле данных и
поле ссылки на следующий элемент, поле ссылки последнего элемента должно
содержать нулевой указатель.
В лвунаправленном списке кажлый элемент содержит поле ланных и два
поля ссылки: Ha предыдущий элемент и на слелующий элемент. Поле ссылки
последнего элемента на следующий элемент и поле ссылки первого элемента
на предыдущий элемент должны содержать нулевой указатель.
К нелинейным структурам данных относятся деревья и сети. Деревья —
это динамические ланные иерархической структуры. Элементы дерева Ha-
зываются вершинами (узлами), каждая вершина имеет ссылки на элементы
слелующего уровня. Здесь рассматривается способ реализации линейных
динамических структур.
Представление линейных динамических структур. Лля созпания пинами-
ческих структур используются переменные типа указатель». Для их реали-
зации необходимо выделить в статической памяти область под два указате-
ля — на начало и конец списка. Сам список размешается в линамической
памяти, что позволяет размешать и обрабатывать большие объемы данных
переменного размера, удалять и вставлять новые элементы. не перемешая
остальные. В отличие от массива звенья списков могут располагаться He B
последовательных участках памяти. Для использования списков необходимо
хранить в статической памяти указатель на начало списка. лополнительная
информация — указатель на послелний элемент списка и количество звеньев.
Для описания элемента списка (звена) используется структура их двух
частей: информационной и апресной. Информационная часть — это элементы
структурного типа, поллежашие обработке (например. геометрические объ-
екты. персоны ит д.). Эти элементы называются информаннионными полями
и могут быть любого типа (ранее объявленното или стандартного). Инфор-
мационных полей может быть несколько.
В апресной части сопержатся указатели (алреса) на другие звенья. В олно-
направленных списках содержится указатель на следующий элемент списка,
в указатель последнего звена записывается нулевой апрес. Такая организация
позволяет просматривать список только в одном направлении.
В лвунаправленных списках содержатся два указателя! на предылущий
элементы и на следующий. Такая организация дает возможность просма-
тривать список в двух направлениях и по апресу текущего звена определить
6/0 Абстрактные HUN! данных и их представление с помощью Kadccod
143
адрес как слепующего, так и предыдущего звена. В общем случае в адресной
части применяется любое количество ссылок. включая массивы. Список —
рекурсивный составной объект. состоящий из двух частей: головы (первый
элемент) и хвоста (список. включающий все последующие элементы). Голова
списка — элемент начала списка.
Преимушеством этих структур является простота реализации улаления
и вставки звеньев. так как нет необходимости в перемещении элементов.
следует лишь изменить значения указателей в соседних звеньях. Нелоста-
TOK списковых структур — отсутствие прямого доступа к элементу списка.
Поскольку пля поиска элемента требуется просмотр списка с самого начала.
Для описания звена списка используется структурный или классовый
тип. который включает в себя информационные поля и поля ссылок. Лля
представления информационного поля используется свое имя типа. Класс
пля представления звеньев однонаправленных списков обобщенно можно
описать типом
class Elem {
public:
fi информационное поле «имя типа данных «идентификатор»;
int п; // номер звена
Elem “ next; // ссылка на следующее
Elem(}{ m=O;
next=0; | //конструктор
Класс лля представления звеньев двунаправленных списков обобщенно
можно описать типом
class Elem {
public:
// информационное поле «имя типа панных <MnesTMiearop>;
int п;//номер звена
'/.ссылкы на следующее и предыдущее эвено
Elem “ next,* prev;
Elem(){n-O; пехЕ-0; prev—O;}
Информационным полем может быть поле классового типа. описываю-
щее объекты предметной области. Полей может быть задано несколько. что
позволяет реализовать простейшую полиморфною модель.
Проектирование однонаправленных списков. Методика работы со списка-
ми не зависиг от типа информационного поля и включает в себя следующие
задачи: создание и просмотр списка. поиск. вставку или удаление элемента в
соответствии с заданным критерием. Список представляется классом List.
полями которого являются два указателя — на первый и последний элементы —
и вспомогательный указатель: Elem * first,* last,* cur;
Основные операции обработки списков — создание, поиск, вставка и
удаление звеньев. Эти операции реализуются методами создания и моли-
фикации списка. Создание осушествляется добавлением элемента в голову
добавлением звена в хвост списка. Обработку и модификацию списка выпол-
няют удалением звена из головы списка, удалением звена из хвоста.
144
Глаза в. Приинины ofsecnniio-opuenmupogdnioed poe pA AM POPs
Описание класса List, методами которого выполняется создание списков:
class List
public:
Flem * first,* last,* cur;
List ()
{first=O;last=0;cur=0; }
void add {int j ); //довавить в хвост
void add head ( int j }; „добавить в голову
void del head | : ИУ удалить из головы
void del xwost{) ;
//удалить из хвоста
%014 dell хинов) ;
void print List ();
//просмотр списка
void del List ( ); //удаление списка
Elem * prev last(); //адрес предпоследнего звена
};
Для создания списка требуется создать первое звено, а затем, используя
базовые методы (добавить в голову} или (добавить в хвост), добавлять слелу-
ющие элементы. В общем случае добавлять звено, так же как и удалять его,
можно в любом месте списка.
Создание нового звена заключается в выполнении лвух операций: первая —
вылеление памяти пол новое звено и запание информационного поля, BTO-
рая — установление связей. При создании очереди адрес нового звена запи-
сывается в поле ссылки предылущего, текуший указатель перехолит на новое
звено, при создании стека апрес нового звена записывается в голову списка,
который хранится в текущем указателе и хранит alpec заглавного звена.
Описание метода — добавление звена в голову списка:
void List::add head ( int j } 1
cur =пем Elem (}; cur->n-j;
ИЕ first == 0){
cur->next=0; first=cur;
last =cur;
}
else {
cur-2next=fiirst; first=cur;
}
Описание метода — добавление звена В XBOCT сСписЕА:
+
i cur->n=j; cur->next=0;
Foid List:irtadd {int
l
e
m
|
cur =new Elem
if( first}|
last->next=cur;
last=cur; }
else {
first=cur;
last =cur; }
Описание метода удаление звена HS головы:
void DListr:del head ( } 1
if({ first->nmext }{
cur=first;
6/0 Adempaxmnvaie muna! данных и их предсмавление с помощью классоя
145
first=first-—>next;
delete cur;
}
else
if (first) {
cur=first;
delete cur;
first=0; last=d0;
}
else cout <<" first=0 "кет:
Метод класса prev _last({) возвращает адрес предпоследнего звена
(используется в методе — удаление звена из хвоста):
EFlem * List::prev last {} {cur=first;
while {(cur->next—>next)
CUrL-—CUL-FHeEXxT г
Feturmh cur,
Описание метода — удаление звена из хвоста:
void List::del xwost ()
{
if (last—frst) {
celete last; first=O;last=-0; }
else |
cursprev_last{);
delete last;
last=cur;
last->next=0;
}
|
Описание метода — удаление списЕа из памяти:
woid Liat::del ITiat (} 1
while {first && last }
del head{);
}
Просмотр списка:
void Listi:print
List {)
{
cur=first;
while[{cur) { cout<<cur-sn<<"
mn cur=cur->next; }
}
Разработанные классы помешаются в отдельный модуль. для однофай-
ловОоЙй программы — над главной функцией. В качестве информационного
поля используется класс, описываюдтий объект «студент»:
class Stud {
char “name:
double bal:
int num;
public:
Stud (}{}
/’ конструктов с параметрами
Stud (char “st, deuble bp, int 11){
паме= new char({ strlen{st}+ 1);
146
Глава 6. Прыииины ofsecnio-opuenmupoddnnoed роса ммнроюаныя
char“ р, “РТ;
post; pl=name;
while [*p!="\0") “pltt=*pt4;
*р1++='\07;
num=n1 ;
bal=bp;
}
deuble get bal({) {return bal; }
int get num(){return num;]
void print({){coute< name << ” “<dhal«<end1;
}
Кол главной функции. в которой cowlaeica CIMCOK объектов:
„/Подключение модуля с описанными классами
void main({) 1
int. inde =, Ge lO: boy 20}:
int indcl[]={3, 4,
Lie 152 19;
char * str [6]=
"ssaaaa”, “ЕЕЁЕ", “аааа”, “ddd", “ими”,
"“темЕ”“};
ты. No; сует. Е =F
List
‘op, spl, мере
зр=пем List(); spl=new List(};
n=5;
// создание списка - добавить в голову
for {1=0; 1<0; 1++) sp-radd head(i);
// создание списка - добавить в хвост
for (i=0; 140; 1++) вр1Т->а99(1);//
cout << “ List spl “ << endl;
spl-sprint List{);
i / просматривается список мы инициализируются
ry// информационные поля
for {i-0, spl->cur-spl-Sfirst;
i<n; itt) {
spl-scur->datl-Stud(str[i], inde[i],
indcl[i]};
spi-scur-spl->cur—>next;
// переход к следующему звену
cout << “ print List Studi” << endl;
spl->cur=spl->first;
while (spl->cur }{
cout sce:
“<< spl->cur->datl.get Ба1{)<< endl;
spl—->cur->datl-print {);
~
spl->cur=spl->cur-
ynext;
} // конеиш главной функции
Для создания списка в главной функции вначале создается пустой список,
далее просматриваются звенья списка и инициализируется их информацион-
ные поля. Для создания списка, т. е. полключения звена. используются оба
метода лобавления звенье.
Построение полиморфной нерархии классов. Для разработки контейнерных
классов, в которых реализуется управление объектами как базовым классом.
так и различных производных классов, используется механизм полиморфизма
{полиморфное общедоступное наслепование и виртуальные функции). В этом
случае в классе Elem не объявляются информационные поля, поскольку он
является базовым и определяет общий интерфейс лля производных классов.
OC Абстрактные minal данных и их представление с помощь классой
147
Методика проектирования рассматривается на примере однонаправленных
списков. Описание класса не включает в себя информационные поля, а
применяет набор виртуальных методов. определяющих интерфейс иерархии
классов. т е. виртуальную функцию printy (|), предназначенную для ото-
бражения полей конкретного объекта:
class Elem {|
public:
Static int on;
Elem “ next;
Elem(} |
next=0; }
wirtual void printyv{)[{
cout <<" Elem count “<< this->n<<endl;
}
wirtual ~Elem(}{}
Инициализация статического поля: int Elem::n=0;
Приведем пример производных классов. в которых используются вирту-
альные функции. В каждом из таких классов виртуальная функция printy ()
замещается своим аспектом для отображения полей конкретного объекта:
class Tnum:public Elem {
public:
float пота;
Tnum(float x) :zElem() {num=z;
}
Tnum(}1}
virtual void printyv()[
cout <<“ Thum num="<< this->num <<endl; }
|
class Rect: public Elem
{
double d, а, в:
public:
Recti) {}
Rect (double 91, double В }:Elem{){ 9=91; ah; }
Virtual void printy()}{
cout <<" print Rect а “<< ac" в= “«<area()<<end1; }
double area() {
return
a-d*a; }
};
В методах класса List для создания списка «лобавить звено B голову
или в хвост» передаются указатели на базовый класс. Иллюстрация МЕТОДИКИ
реализации опнонаправленного списка:
class List {
public:
Elem “ first,* last,* cur;
List ()
{ffirst=O;last=0;cur-0; }
void addy ( Elem “temp );
void add heady (Elem “*temp);
void print Listv();
|
148
Глава 6. Приинины ofsecniio-opuenmupodaniocd прозраммнрования
Описание метода — добавление звена в хвост списка! указатель переда-
ется на базовый класс Elem:
woid List::addy ( Elem “temp ) {|
tTemp->nt+;
if( first == 0) {temp-snext—0; first-temp;
last -temp; |}
else 1
last— snext—temp;
last =temp;
temp->next—0; }
Описание метода — добавление звена в голову списка: указатель пере-
дается на базовый класс Elem:
void List::add_headv ( Elem *temp ) {ff
temp->n++;
iF i( first == O}f{
temp->next=0; first=temp; last =temp;
}
else { temp->next=first;
first=temp;
}
Описание метода — просмотр списка, в котором вызывается виртуальная
фунекпия вывола значений полей производных классов:
void List::print Listvy() { cur=first;
while{cur)] {
cur->printv();
// вызывается аспект полиморфной функции
cur=cur->next;|
В главной функции используются указатели на базовый класс, к которым
применяется принцип полиморфизма: указатель на базовый класс может
ссылаться на объект производного класса. Ниже рассматривается созпание
списка, содержащего смесь объектов:
//Подключение модуля с описанными классами
void main{){
List spm;
Elem “py;
ponew Rect(5, Ti;
spm.addyv (р):
pSnew Tnum(257)
spm.add БеаЧу {р};
Б=пем Tnuam({101)
spm.add headv({p);
=
.
o
e
penew Rect({15, 4);
spm.addvyv (p);
ponew Rect (346, 17); spm.addyv (р):
cout<< spm.first->n<< endl;
spm.print Listv()};
Проектирование двунаправленных списков. Двунаправленный список пред-
ставляется классом List, формально аналогичным классу опнонаправленного
списка. но методы его создания и обработки принципиально другие. Полями
Нового класса, описывающего двунаправленный список, также являются два
указателя (на первый и последний элементы) и вспомогательный указатель:
5. И. Абстрактные Hine! данных и их представление с помощью классой
149
Elem * first,* last,* cur; В методах реализуются основные действия
по созданию списка. В отличие от предылущего списка в методах (добавле-
ние звена в голову или добавление звена хвост) используется альтернатив-
ный способ задания информационной части звена (информационное поле
передается в методы в качестве параметра).
В качестве информационного поля звена списка применяется конкрет-
ный тип Point. Объект этого типа передается в конструктор. Описание
класса звена:
class Elem {
public:
Point data ; // информационное пол
int п; // номер звена
Elem * next, *рхеу;//ссылки на следующее и предыдущее звено
Elem {Point d){
data= а n=O; next=0; prev=0;}
//конструктор
woid print()[{ data.print{);
}
Е
Описание класса списка:
class List {
public:
Elem “ first,* last,* cur;
List(){first=0; last=0;
cur-0; }
void add (Point temp ); // добавить в хвост
void add head (Point temp); // побавить в голову
void del xuwest(}) : // удалить из хвоста
void del head ( |: // удалить из головы
int print List prev(); //UpocmMoTp списка с конца
void print _List{}; //просмотр списка с конца
void del_List [ ); //удалить список
i
При созлании звена выполняются лве операции: выделение памяти под
новое звено и залание информационного поля: установление связей. Ina
полключения нового звена в хвост списка необхолимо установить значения
указателей связи.
В поле ссылки на прелыпушее звено у нового звена записывается апрес
последнего звена:
cur->prev—last;
В поле ссылки Ha слепующее SECHO у последнего звена записывается
адрес нового:
1ТазЕ-зпенЕЕсоые;
Адрес текущего звена переходит на новое звено: Last=cur; Новое звено
становится последним — XBOCTOM COMCKa.
150
Глава в. Приннины объектио-ориенеироватиного проераммирования
Описание метода — лобавление В ХВОСТ:
void Т115+::ааа { Point temp ) {
cur =new Elem
(Point);
iE
first} {
last-snextocur; cur->prev—last;
last=cur;
cur->next=0; |
else
{
cur->prev=0;
cur->next=0;
first=cur;
last =cur; }
Рассмотрим установление связей при записи звена B ГОЛОЕБУу списка.
В поле ссылки на следующее звено у нового звена записывается адрес первого:
cur->next-irst;
В поле ссылки пе рвого звена на Предылушее звено записывается адрес нового:
first->prev—cur;
Апрес первого звена переходит Ha новое звено: first=cur;
Описание метода — добавление звена в голову:
void List::tadd head ( Point temp }
{
cur =new Elem (Point |;
cet if first == O){frst=-cur;last =cur;
else {1
cur->next=fiirst;
first->prev—cur;
first=cur; }
При удалении звена из головы или хвоста устанавливаются связи и OCBO-
божлается память. Для удаления из головы в поле ссылки звена, следующего
за первым, устанавливается нулевой адрес:
first—>next—>prev-0;
Ampec первого переходит Ha следующее звено
first=first->next:
Описание метода — удаление звена из головы:
woid List:: del head [ } j{
Elem “tmp;
if (first->next )1
tmp=first; first->next—>prev=0;
first=first->next; delete tmp;
}
else
if (first) {
tmp-first; delete tmp;
first=0; last=0; }
else
cout <<" first=0 ‘"<eendl]:
Для удаления звена из XBOCTd в поле ссылки Ha следуюущее звено у Пред-
последнего звена устанавливается нулевой ampec:
6/0. Абетрактные мнипы данных и их Apedecmakienue с MOMOWbD классов
151
last-sprev—- snext—O0;
Адрес последнего переходит Ha адрес предыдушего звена:
last=last->prev;
void List::del xwost()
{
Flem “tmp;
if(last—>prev) {
tmp=last; last->prev->next=0;
Jast—last—>prev; delete tmp;
else if(last){
tmp—-last; delete tmp;
first=0 ; last=0;
}
else cout << “ last=0 “" << endl:
Просмотр с конца списка:
int List::print
List prev()
{
cur-last ; int 1-0;
while (cur!=6) {
ЕР:
cur—>+print ()<<" i
cur —cur—>prev; }
return jr
s
e
Освобожление памяти:
void List::del List { ) {while (first && last) del_head{);}
Метолика созлания лвунаправленных списков. После полключения модуля
с прелставленными выше классами созлание списков выполняется в главной
функции. Информационное поле перелается методам как параметр. Для те-
стового примера используется чтение из массива констант. Модули с описа-
ниями рассмотренных выше классов полключаются директивой include:
n—4; List
Sp, Sppr
(57,27), Point 245,67), Point (12,34),
лание списка — добавить в голову
it+)
[i]);
списка — добавить в хвост
+
Для тестирования описания классов Point, Elem. List должны быть
помещены в отлельные молули либо перед главной функцией. Созданные
списки можно обрабатывать: объелинять. удалять элементы сортировать и т. д.
152
Глана 6. Принцимы объектио-ориелтированного нуюераммирювания
6.11. Обобщенная таблица приоритетов операций С++
В заключение представляется обобщенная таблица приоритетов опера-
ний. Операции в С++ выполняются в соответствии со своими приоритета-
ми. Изменить порялок выполнения можно с помошью скобок. Операции.
расположенные в одной строке, имеют один и тот же приоритет, строки
расположены в порядке убывания приоритета:
'! — разрешение области действия;
++, --—, —>, 0,0, (очка) — постинкремент и постдекремент;
' F &, sizeof(), ne — *(разадресация), ++ (префикс), ——, new , delete —
унарные,
+,-,*, /, % — арифметические;
<, <=, >, >= == (равно), '= (неравно) — сравнение:
&&, | — логические"
Е
— = = — присваивания;
‚ — операция запятая.
6.12. Задания для самостоятельного выполнения
Задание 1. Определить класс, описывающий объект «студент» с именем
stud и солержаший слелующие поля: фамилия, горол, номер грушты. ко-
личество оценок, массив с оценками и метолы (вычисление срелнето Gama
студента, проверка на напичие хотя олной неуловлетворительной оценки.
проверки того, что студент отличник, начисление стипендии).
Разработать следующие подпрограммы функции:
* создания динамического массива студентов с использованием чтения
ланных из бинарного (текстового) файла:
* функцию, формирующую динамическую матрицу (курс). в которой в
кажлой строке объелинены студенты одной группы:
- функцию, формирующую массив из срелнего балла кажлой группы:
* фунцию, вычисляющую срелний балл курса:
- фунции, вычисляющие численность двоечников и отличников:
* функцию сортировки массива по среднему баллу и записи этого мас-
в бинарный файл.
Залание 2. Опрелелить классы, описывающие объекты точку линию».
окружность. Метолами классов «Линии» и «окружности» являются фунЕЦИи
проверки положения точки относительно этих объектов. Разработать пол-
программы функции, формирующей матрицу кажлый элемент которой лля
кажлой точки опрелеляет ее положение относительно линии или окружности.
Залание 3. Описать класс многоугольника. полями которого являются
указатель на классовый тип «точка» и количество верптин Метолы класса —
метолы вычисления периметра и плошади.
Залание 4. Созлать списки из элементов. описывающие объекты: точкх
окружность. Разработать полпрограмму формирования матрицы. кажлый
элемент которой пля кажлой точки опрелеляет сё положение относительно
окружности.
КОНТРОЛЬНЫЕ ВОПРОСЫ И ЗАДАНИЯ
1. Перечислите основные базовые типы языка н назовите характеристики
простых переменных.
2. Каким правилам подчиняются арифметические выражения?
3. Что такое пинейный алгоритм? Дайте определение разветвляющихся
aTOpHTMOs.
4. Что такое оператор присваивания, условный оператор, составной one-
ратор и вложенные условные операторы?
5. Дайте определение операторов цикла while, repeat, for.
6. Чем итерапионные циклы отличаются от циклов с неизвестным чис-
лом повторений?
7. Чем различаются циклические алгоритмы с предусловием и с постус-
ловием?
8. В чем заключается запача табуляции функции? Напишите программу
9. Назовите принципы структурного программирования.
10. Что такое составные типы и структурные переменные?
11. Как описываются статические многомерные массивы? Как опреде-
ляется лоступ к элементу?
12. Дайте определение указателя. Что такое разалресания указателя?
13. Какие основные операции выполняются нал указателями?
14. Kak описываются динамические массивы? Каким образом осушест-
вляются поступы по указателю и по индексу к элементу массива”
15. Назовите алгоритмы сортировки и напинтите кол.
16. Как выцелпяется память под пинамические многомерные массивы?
17. Напишите кол решения базовых запач' определение сумм и произве-
дений строк и столбцов элементов матрицы (или индексов) по заданным
ЕрИТериям.
18. Дайте определение подпрограммы в С++. Что такое интерфейс?
19. Какие сушествуют механизмы передачи параметров в подпрограммы
функции?
20. Что такое ссылки? Каким образом выполняются передачи по ссылке
и по указателю?
21. Что такое матричные операции?
22. Опишите алгоритм выделения лексем из предложения.
23. Что такое структура и структурные переменные? Как обратиться к
полю структуры?
24. Что такое смесь данных, ее представление”
25. Каким образом происходят передача и возврат структур по ссылке и
по указателю при вызове функций? Как формируется стек вызовов”?
154
Aoampe init sorbate ы Jaiaaus
26. Дайте определение пользовательского
типа — класса членов класса
(полей. методов). Что такое инкапсуляпия?
27. Что такое конструкторы и леструеторы класса?
28. Еаким образом осушествляется инидиализадия полей при отеРЫТОМ
и закрытом поступах”
29. Каким образом выполняются присваивание объектов. передача и в0з-
враг в функции объектов?
30. Что такое поток? Kak созлать потоковый
файл?
31. Как прочитать текстовый файл?
32. Как создать и прочитать бинарный файл?
33. Что означает прямой поступ к компоненту файла и как его осуше-
CTEHTE?
34. Как можно заменить компоненты бинарного файла?
35. Что собой представляют списки и метолы их построения и обработки?
ЛИТЕРАТУРА
Вирт A. Алгоритмы + структуры данных = программы. М.: Мир. 1985.
Иванова ГС.. Ныичушкына Т.Ы., Пугачеи Е.К. Объектно-ориентированное про-
траммирование. М.: Изд-во МГТУ им. Н.Э. Бяумана, 2001.
Керниган 5., Ричи Л. Язык программирования Си. М.: Вильямс, 2009
Лафюре Р. Объектно-ориентированное программирование в С++. Классика
Computer Science. СП6.: Питер, 2003.
Либерти Л. Освой самостоятельно C++ за 21 день. М.: Вильямс. 2000.
Ничушкина Т.Е. Разработка программ рекурсивной структуры. М.: Изл-во
MITY им. Н.Э. Блумана, 2019.
Павлояская Т. С/С++. Программирование на языке высокого уровня. СПб.:
Питер. 2001.
Павловская ТГ, Шупак AO. С/С++. Структурное программирование. СПб.:
Питер. 2002.
Подбельский В.В Язык Си++. М.: Финансы и статистика, 7003.
Русакова 3.Н. Динамические структуры панных и вычислительные алгоритмы
Visual C++. СП6.: Образовательные проекты, 2013.
Страуструн Б. Язык программирования С++. СПб.: Невский диалект, 1999.
Аортон А. Visual (C++. Базовый курс. М.: Вильямс. 2007.
Шилат Г. Полный справочник по С++. М.: Вильямс. 2006.
Шилот Г. Теория и практика C++. СПб.: ВНУ. 1996.
MUpesSani cg ea 2c АРКА ad bagede АН ocavec ИАА А РАНЕНИЕ 3
MPR RN
а
ЕЕ
Е cdbanxsscumnsabdaninvdedueds 5
Глава 1. Основные понятия С++. Алгоритмы обработки простых
ПОСПЕДОВАТЕельностей ........аналаниниваиаианиланьнавилани
рва ианнвии шин иташи а овнаашия 6
[.1. Типы данных и переменные. Структура программы Ha языке С++.
Линейные алгоритмы.........-озлаааинонаии
ланолин ни валия ви пиалии оз пашша ноша но нивашия 6
1.2. Логические выражения. Разветвляющиеся алгоритмы.
УСЛОВНЫЕ ТО оао
ааа ранее ИОВ 11
1.3. Итерационные циклы. Операторы цикла oo.
аааа ана нала ши льцишия 16
1.4. Организация счетных и итерационных циклов ....-... алан нанания 21
1.5. Принципы структурного программирования ...............
secon 23
1.6. Задания для самостоятельного выполнения ........-
ила. ланининаниаиннь 23
Глава 2. Способы представления структур данных. Составные типы ....... 25
2.1. Статические массивы. Статическое связывание...........
ил ьеньниланыя 26
2.2. Статические многомерные массивы. MaTpntulbl .............0000...000...02. 30
2.3. Динамические переменные ........... ааа инизачи линия ионная нена ани панили ни 33
2.4. Адресная арифметика. Указатели и динамические массивы ........ 37
2.5. Динамические многомерные массивы .... лили линилаланииаишинининиииоинишаная 49
2.6. Алгоритмы и программы обработки динамических матриц ......... 53
2.7. Задания для самостоятельного выполнения oo... cece 59
Глава 3. Принципы разработки полпрограмм. Полпрограммы
О
О sedate ye esuensuevendsccvuanen 60
3.1. Принципы разработки подпрограмм. Интерфейс подпрограммы.
Механизм передачи параметров. Области видимости ........... ааа 60
3.2. Разработка подпрограмм в С++. Функции wooo...
eee 64
3.3. Вызов функции. Перелача простых переменных 2... 66
3.4. Перелача в функцию аргументов составных типов .............. ааа 71
3.5. Передача в функцию многомерных массивов ou... ee 76
3.6. Матричные операции ......_. ааа алии ии пиши шили нина шила ци ими линия 81
3.7. Указатели на ФУНКЦИИ ........ неа мо сонет нинининиюии иронии ииаининнии 83
3.8. Задания для самостоятельного выполнения ..........
ла ениалииииннная 85
ОГЛАВЛЕНИЕ
Оглавление
157
Глава 4. ee
ere eee завершающим
4.1. РНЕ переменные. АНН строки ................ 86
42. Основные приемыи
(DUR ass
90
44. Задания для самостоятельного выполнения .НЕА ОВЕН АА 100
Пава 5. Пользовательские типы. Структуры.....................ллииииишитоининьюивитьниь 101
5.1. Синтаксис опрелеления структуры. Стуктурные переменные .....
5.2. Передача структурных переменных в функции. Массивы
структурных переменных... a
fee
sesassnsecsocnnnИ
5.3. Многофайловые проекты, структура и ` связь модулей .pee cane ae 113
5.4. Залания для самостоятельного выполнения ............02000c0ceeresenenererees 114
Глава 6. Принципы объектно-ориентированного программирования ..... 116
6.1. Введение в классы С++. Определение класса .......„ееенинононониниюания 116
6.2. Конструктор и деструктор класса .......:00:cccccsscanecsesneerersenssessoncensense 119
6.3. Создание экземпляров класса. Доступ к членам класса ............00. 121
6.4. Массивы экземпляров класса ..2...ccccccsccccssssesecessssecseccsencseseneeesesees 124
6.5. ВКЛЮЧЕНИЕ И КОМПОЗИЦИЯ ....0000sccsscscersssesnerssienresseansessessessoatessnenseane 125
6.6. Наследование. Доступ к базовому классу..............шишлиииииииининиминиюнишь 128
6.7. Полиморфное общедоступное HACTCIOBAHHE ............0ccccccenerereceeeeeee 132
6.8. Виртуальные функции и сложный динамический
то
О НЕЕ НИНЫ
a
tee 134
6.9. Потоковые файлы С++. Создание и обработка ...........ccccccccceesee 137
6.10. Абстрактные типы данных и их представление с помошью
ока ЕЕ ИАН ИЗДЕЛИЕ
a es ЕЛИ142
6.11. Обобщенная таблица приоритетов операций С++...........ззхижикикия 152
6.12. Задания для самостоятельного выполнения... иикикижютсекинежижикия 152
Контрольные вопросы И Задяния ......cccccccenerersrssesssssnsneneesesnenensnenenenssesssesanes 153
ЕЕН
РНИИ ИНАЧЕ 155
Учебное издание
Русакова Зинаила Николаевна
Рудаков Игорь Владимирович
Структуры данных в С++
Релактор Е.Н. Соколова
Художник Э Ш. Мурадова
Корректор Е.А. Фетисова
Компьютерная верстка Е.В. Жуковой
Оригинал-макет полготовлен
в Излательстве МГТУ им. Н.Э. Баумана.
В оформлении использованы шрифты
Студии Артемия Лебедева.
Подинсано в псчать 20.04.2023. Формат 70100,16.
Усл. печ. л. 12,84. Тираж 124 экз.
Издательство МГТУ им. Н.Э. Баумана.
105005, г. Москва, улица 2-я Бауманская,д. 5, стр. 1.
info@bmstu.press https: //bmstu.press
Отиечатано в типографии МГТУ им. Н.Э. Баумана.
105005, г. Москва, улица 2-я Бауманская,д. 5, стр. 1.
baumanprint@zgmailcom