Lab01 - Белорусско-Российский университет

advertisement
Министерство образования Республики Беларусь
Министерство образования и науки Российской Федерации
ГУВПО “Белорусско-Российский университет”
Кафедра “Автоматизированные
cистемы управления”
Дисциплина “Объектно-ориентированное программирование
и проектирование”
Лабораторная работа № 1
Программирование с использованием классов. Конструкторы и деструкторы.
Время выполнения работы – 4 часа
2014
2
1 Цель работы
Получение навыков в разработке программ с использованием классов.
2 Техническое обеспечение
1.1
1.2
1.3
1.4
Персональная ЭВМ с процессором Pentium 2 и более поздних моделей.
Клавиатура.
Дисплей.
Печатающее устройство.
3 Программное обеспечение
3.1 Операционная система Windows 2000 и более поздние версии.
3.2 Система программирования Visual C++ версия 6.0 SP6 и более поздние версии.
4 Постановка задачи
4.1 Реализовать задачи 2.1 и 2.2, приведенные ниже.
4.2 Разработать консольное приложение согласно варианту, приведенному в разделе 7.
5 Содержание отчета
5.1 Тема и цель работы.
5.2 Текст программы.
5.3 Результаты выполнения программы.
6 Общие сведения
6.1 Появление ООП  реакция на кризис программного обеспечения
Объектно-ориентированное программирование (ООП) — это технология, возникшая
как реакция на очередную фазу кризиса программного обеспечения, когда методы структурного программирования уже не позволяли справляться с растущей сложностью промышленного программного продукта. Следствия  срыв сроков проектов, перерасход бюджета, урезанная функциональность и множество ошибок.
Существенная черта промышленной программы  ее сложность: один разработчик
не в состоянии охватить все аспекты системы, поэтому в ее создании участвует целый коллектив. Следовательно, к первичной сложности самой задачи, вытекающей из предметной
области, добавляется управление процессом разработки с учетом необходимости координации действий в команде разработчиков.
Так как сложные системы разрабатываются в расчете на длительную эксплуатацию,
то появляются еще две проблемы: сопровождение системы (устранение обнаруженных ошибок) и ее модификация, поскольку у заказчика постоянно появляются новые требования и
пожелания. Иногда затраты на сопровождение и модификацию сопоставимы с затратами на
собственно разработку системы.
Способ управления сложными системами был известен еще в древности  divide et
impera (разделяй и властвуй). То есть выход — в декомпозиции системы на все меньшие и
меньшие подсистемы, каждую из которых можно совершенствовать независимо. Здесь вы,
наверное, вспомните о методе нисходящего проектирования, которым мы активно пользовались в первой книге практикума. Но если в рамках структурного подхода декомпозиция понимается как разбиение алгоритма, когда каждый из модулей системы выполняет один из
этапов общего процесса, то ООП предлагает совершенно другой подход.
Суть его в том, что в качестве критерия декомпозиции принимается принадлежность
ее элементов к различным абстракциям проблемной области. Откуда же берутся эти аб-
3
стракции? Исключительно из головы программиста, который, анализируя предметную область, вычленяет из нее отдельные объекты. Для каждого из этих объектов определяются
свойства, существенные для решения задачи. Затем каждому реальному объекту предметной области ставится в соответствие программный объект.
Почему объектно-ориентированная декомпозиция оказалась более эффективным
средством борьбы со сложностью процессов проектирования и сопровождения программных систем, чем функциональная декомпозиция? Тому есть много причин. Чтобы в них
разобраться, рассмотрим критерии качества проекта, связанные с его декомпозицией.
6.2 Критерии качества декомпозиции проекта
Со сложностью приложения трудно что-либо сделать  она определяется целью создания программы. А вот сложность реализации можно попытаться контролировать. Первый вопрос, возникающий при декомпозиции: на какие компоненты (модули, функции,
классы) нужно разбить программу? Очевидно, что с ростом числа компонентов сложность
программы растет, поскольку необходима кооперация, координация и коммуникация между
компонентами. Особенно негативны последствия неоправданного разбиения на компоненты, когда оказываются разделенными действия, по сути тесно связанные между собой.
Вторая проблема связана с организацией взаимодействия между компонентами. Взаимодействие упрощается и его легче взять под. контроль, если каждый компонент рассматривается как некий «черный ящик», внутреннее устройство которого неизвестно, но известны выполняемые им функции, а также «входы» и «выходы» этого ящика. Вход компонента
позволяет ввести в него значение некоторой входной переменной, а выход  получить значение некоторой выходной переменной. В программировании совокупность входов и выходов черного ящика определяет интерфейс компонента. Интерфейс реализуется как набор
некоторых функций (или запросов к компоненту), вызывая которые клиент либо получает
какую-то информацию, либо меняет состояние компонента.
Модное нынче словечко «клиент» означает просто-напросто компонент, которому
понадобились услуги другого компонента, исполняющего в этом случае роль сервера. Взаимоотношение клиент/сервер на самом деле очень старо и использовалось уже в рамках
структурного программирования, когда функция-клиент пользовалась услугами функциисервера путем ее вызова.
Подытожим сказанное о проблемах разбиения программы на компоненты и организации их взаимодействия. Для оценки качества программного проекта нужно учитывать,
кроме всех прочих, следующие два показателя:
Q Сцепление (cohesion) внутри компонента — показатель, характеризующий степень
взаимосвязи отдельных его частей. Простой пример: если внутри компонента решаются две
подзадачи, которые легко можно разделить, то компонент обладает слабым (плохим) сцеплением.
□ Связанность (coupling) между компонентами — показатель, описывающий интерфейс между компонентом-клиентом и компонентом-сервером. Общее число входов и выходов сервера есть мера связанности. Чем меньше связанность между двумя компонентами,
тем проще понять и отслеживать в будущем их взаимодействие. А так как в больших проектах эти компоненты часто разрабатываются разными людьми, то очень важно уменьшать
связанность между компонентами.
Заметим, что описанные показатели, конечно, имеют относительный характер, и
пользоваться ими следует благоразумно. Например, фанатичное следование первому показателю (сильное сцепление) может привести к дроблению проекта на очень большое количество мелких функций, и сопровождающий программист вряд ли помянет вас добрым словом.
Почему в случае функциональной декомпозиции трудно достичь слабой связанности
между компонентами? Дело в том, что интерфейс между компонентами в таком проекте реализуется либо через глобальные переменные, либо через механизм формальных/фактических
параметров. В сложной программной системе, реализованной в рамках структурной пара-
4
дигмы, практически невозможно обойтись без связи через глобальные структуры данных, а
это означает, что фактически любая функция в случае ошибки может испортить эти данные.
Подобные ошибки очень трудно локализуются в процессе отладки. Добавьте к этому головную боль для сопровождающего программиста, который должен помнить десятки (если не
сотни) обращений к общим данным из разных частей проекта. Соответственно, модификация
существующего проекта в связи с новыми требованиями заказчика также потребует очень
большой работы, так как возникнет необходимость проверить влияние внесенных изменений
практически на все компоненты проекта.
Другой проблемой в проектах с функциональной декомпозицией было «проклятие»
общего глобального пространства имен. Члены команды, работающей над проектом, должны
были тратить немалые усилия по согласованию применяемых имен для своих функций, чтобы они были уникальными в рамках всего проекта.
6.3 Что принесло с собой ООП
Первым бросающимся в глаза отличием ООП от структурного программирования является использование классов. Класс  это тип, определяемый программистом, в котором
объединяются структуры данных и функции их обработки.
Конкретные переменные типа данных «класс» называются экземплярами класса, или
объектами. Программы, разрабатываемые на основе концепций ООП, реализуют алгоритмы,
описывающие взаимодействие между объектами.
Класс содержит константы и переменные, называемые полями, а также выполняемые
над ними операции и функции. Функции класса называются методами. Предполагается, что
доступ к полям класса возможен только через вызов соответствующих методов. Поля и методы являются элементами, или членами класса.
Эффективным механизмом ослабления связанности между компонентами в случае
объектно-ориентированной декомпозиции является так называемая инкапсуляция.
Инкапсуляция  это ограничение доступа к данным и их объединение с методами, обрабатывающими эти данные. Доступ к отдельным частям класса регулируется с помощью
специальных ключевых слов: public (открытая часть), private (закрытая часть) и protected (защищенная часть).
Методы, расположенные в открытой части, формируют интерфейс класса и могут
свободно вызываться клиентом через соответствующий объект класса. Доступ к закрытой
секции класса возможен только из его собственных методов, а к защищенной  из его собственных методов, а также из методов классов-потомков.
Инкапсуляция повышает надежность программ, предотвращая непреднамеренный
ошибочный доступ к полям объекта. Кроме этого, программу легче модифицировать, поскольку при сохранении интерфейса класса можно менять его реализацию, и это не затронет
внешний программный код (код клиента).
С понятием инкапсуляции тесно связано понятие сокрытия информации. С другой
стороны, понятие сокрытия информации соприкасается с понятием разделения ответственности между клиентом и сервером. Клиент не обязан знать, как реализованы те или иные
методы в сервере. Для него достаточно знать, что делает данный метод и как к нему обратиться. При хорошем проектировании имена методов обычно отражают суть выполняемой
ими работы, поэтому чтение кода клиента для сопровождающего программиста превращается просто в удовольствие, если не сказать — в наслаждение.
Заметим, что класс одаривает своего программиста-разработчика надежным «укрытием», обеспечивая локальную (в пределах класса) область видимости имен. Теперь можно сократить штат бригады программистов: специалист, отвечающий за согласование имен функций и имен глобальных структур данных между членами бригады, стал не нужен. В разных
классах методы, реализующие схожие подзадачи, могут преспокойно иметь одинаковые
имена. То же относится и к полям разных классов.
5
С ООП связаны еще два инструмента, грамотное использование которых повышает
качество проектов: наследование классов и полиморфизм.
Наследование — механизм получения нового класса из существующего. Производный класс создается путем дополнения или изменения существующего класса. Благодаря
этому реализуется концепция повторного использования кода. С помощью наследования
может быть создана иерархия родственных типов, которые совместно используют код и интерфейсы.
Полиморфизм дает возможность создавать множественные определения для операций
и функций. Какое именно определение будет использоваться, зависит от контекста программы. Вы уже знакомы с одной из разновидностей полиморфизма в языке C++ — перегрузкой
функций. Программирование с классами предоставляет еще две возможности: перегрузку
операций и использование так называемых виртуальных методов. Перегрузка операций позволяет применять для собственных классов те же операции, которые используются для
встроенных типов C++. Виртуальные методы обеспечивают возможность выбрать на этапе
выполнения нужный метод среди одноименных методов базового и производного классов.
Кроме наследования, классы могут находиться также в отношении агрегации: например, когда в составе одного класса имеются объекты другого класса. Совместное использование наследования, композиции и полиморфизма лежит в основе элегантных проектных
решений, обеспечивающих наибольшую простоту модификации программы (эта тема будет
рассмотрена на втором семинаре).
Ну и, наконец, отметим, что в реальном проекте, разработанном на базе объектноориентированной декомпозиции, находится место и для алгоритмически-ориентированной
декомпозиции (например, при реализации сложных методов).
6.3.1 От структуры  к классу
Прообразом класса в C++ является структура в С. В то же время в C++ структура обрела новые свойства и теперь является частным видом класса, все элементы которого по
умолчанию являются открытыми. Со структурой struct в C++ можно делать все, что можно
делать с классом. Тем не менее в C++ структуры обычно используют лишь для удобства работы с небольшими наборами данных без какого-либо собственного поведения.
Новые понятия легче изучать, отталкиваясь от уже освоенного материала. Давайте
возьмем задачу 6.1 из первой книги практикума и посмотрим, что можно с ней сделать, применяя средства ООП.
6.4 Задача 2.1. Поиск в массиве структур
В текстовом файле хранится база отдела кадров предприятия. На предприятии 100
сотрудников. Каждая строка файла содержит запись об одном сотруднике. Формат записи: фамилия и инициалы (30 позиций, фамилия должна начинаться с первой позиции), год
рождения (5 позиций), оклад (10 позиций). Написать программу, которая по заданной фамилии выводит на экран сведения о сотруднике, подсчитывая средний оклад всех запрошенных
сотрудников.
Напомним, что в программе, предложенной для решения задачи 6.1 (П1), для хранения сведений об одном сотруднике была использована следующая структура Ma n :
str u ct Ma n {
c ha r na me [l _ na me + 1] ;
i nt bir th _ ye a r ;
f loa t pa y ;
};
Начнем с того, что преобразуем эту структуру в класс, так как мы предполагаем, что
наш новый тип будет обладать более сложным поведением, чем просто чтение и запись его
полей:
6
cla ss Ma n {
c ha r na me [l _ na me + 1] ;
n t b irt h _ ye a r :
f loa t pa y ;
};
Замечательно. Это у нас здорово получилось! Все поля класса по умолчанию  закрытые (pr iva te ). Так что если клиентская функция ma in() объявит объект Ma n ma n , а потом
попытается обратиться к какому-либо его полю, например: ma n.pa y = va l ue , то компилятор
быстро пресечет это безобразие, отказавшись компилировать программу. Поэтому в состав
класса надо добавить методы доступа к его полям. Эти методы должны быть общедоступными, или открытыми (pu bli c ).
Однако предварительно вглядимся внимательнее в определения полей. В решении задачи 2.1 поле n a me объявлено как статический массив длиной l_n a me + 1 . Это не очень
гибкое решение. Мы хотели бы, чтобы наш класс Man можно было использовать в будущем в
разных приложениях. Например, если предприятие находится в России, то значение l_ na me
= 3 0 , по-видимому, всех устроит, если же приложение создается для некой восточной страны, может потребоваться, скажем, значение l_ na me = 20 0 . Решение состоит в использовании динамического массива символов с требуемой длиной. Поэтому заменим поле cha r
na me [l _ na me + 1 ] на поле c ha r * p Na m e . Сразу возникает вопрос: кто и где будет выделять
память под этот массив? Вспомним один из принципов ООП: все объекты должны быть самодостаточными, то есть полностью себя обслуживать.
Таким образом, в состав класса необходимо включить метод, который обеспечил бы
выделение памяти под указанный динамический массив при создании объекта (переменной
типа Man). Метод, который автоматически вызывается при создании экземпляра класса,
называется конструктором. Компилятор безошибочно находит этот метод среди прочих методов класса, поскольку его имя всегда совпадает с именем класса.
Парным конструктору является другой метод, называемый деструктором, который
автоматически вызывается перед уничтожением объекта. Имя деструктора отличается от
имени конструктора только наличием предваряющего символа ~ (тильда).
Ясно, что если в конструкторе была выделена динамическая память, то в деструкторе
нужно побеспокоиться об ее освобождении. Напомним, что объект, созданный как локальная
переменная в некотором блоке { }, уничтожается, когда при выполнении достигнут конец
блока. Если же объект создан с помощью операции new, например:
Ma n * p Ma n = ne w Ma n ;
то для его уничтожения применяется операция delete, например: delete pMan. Итак, наш
класс принимает следующий вид:
cla ss Ma n {
pu bli c :
Ma nC i nt IN a me = 3 0) { p Na me = new c ha r[l Na m e + 1] ;} // к он с тру к то р
~ Ma n() { de l e te [] pNa me ; }
// де стр у кт ор
pri va te :
c ha r* p Na me ;
i nt bir th _ ye a r ;
f loa t pa y ;
};
Обратим ваше внимание на одну синтаксическую деталь  объявление класса должно
обязательно завершаться точкой с запятой (; ). Если вы забудете это сделать, то получите от
компилятора длинный список маловразумительных сообщений о чем угодно, но только не об
истинной ошибке. Что поделаешь, таков уж наш компилятор...
Рассмотрим теперь одну важную семантическую деталь: в конструкторе класса параметр l Na me имеет значение по умолчанию (3 0 ). Если все параметры конструктора имеют
значения по умолчанию или если конструктор вовсе не имеет параметров, он называется
7
конструктором по умолчанию. Зачем понадобилось специальное название для такой разновидности конструктора? Разве это не просто удобство для клиента — передать некоторые
значения по умолчанию одному из методов класса? Нет! Конструктор — это особый метод, а
конструктор по умолчанию имеет несколько специальных областей применения.
Во-первых, такой конструктор используется, если компилятор встречает определение
массива объектов, например: Ma n m a n [2 5] . Здесь объявлен массив из 25 объектов типа Man,
и каждый объект этого массива создан путем вызова конструктора по умолчанию! Поэтому
если вы забудете снабдить класс конструктором по умолчанию, то вы не сможете объявлять массивы объектов этого класса.
Второе применение конструкторов по умолчанию относится к механизму наследования классов. Об этом мы будем говорить на втором семинаре.
Вернемся к приведенному выше описанию класса. В нем методы класса определены
как встроенные (i nl i ne ) функции. При другом способе методы только объявляются внутри
класса, а их реализация записывается вне определения класса, как показано ниже:
// Ma n . h (и нте рфе й с кл а с са )
cla ss Ma n {
pu bli c :
Ma n( in t l Na me = 3 0) ; // кон ст р у кто р
~ Ma n() ;
// де стр у кт ор
pri va te :
c ha r* p Na me ;
i nt bi rt h _y e a r ;
lo a t pa y ;
};
// Ma n . cpp (р е а л иза ц ия кл а с са )
#i n cl u de "M a n . h"
Ma n : : Ma n (i nt l Na me ) { p Na me = ne w c ha r[ l Na m e + 1] ; }
Ma n : : ~ Ma n () { d e le t e [] p Na me ; }
При внешнем определении метода перед его именем указывается имя класса, за
которым следует операция доступа к области видимости :: . Выбор способа определения метода
зависит
в
основном
от
его
размера:
короткие
методы
можно
определить как встроенные, что может привести к более эффективному коду.
Впрочем, компилятор все равно сам решит, может он сделать метод встроенным
или нет.
Продолжим процесс проектирования интерфейса нашего класса. Какие методы нужно
добавить в класс? С какими сигнатурами? На этом этапе очень полезно задаться следующим
вопросом: какие обязанности должны быть возложены на класс Man?
Первую обязанность мы уже реализовали: объект класса хранит сведения о сотруднике. Чтобы воспользоваться этими сведениями, клиент должен иметь возможность получить эти сведения, изменить их и вывести на экран. Кроме этого, для поиска сотрудника
желательно иметь возможность сравнивать его имя с заданным.
Начнем с методов, обеспечивающих доступ к полям класса. Для считывания значений
полей добавим методы Ge t Na me () , Ge tB i rt hYea r() , Get Pa y() . Очевидно, что аргументы
здесь не нужны, а возвращаемое значение совпадает с типом поля.
Для записи значений полей добавим методы Set N a me( ) , SetB irt h Y ea r() , Se tPa y( ).
Чтобы определиться с сигнатурой этих методов, надо представить себе, как они будут вызываться клиентом. Напомним, что в задаче 6.1 (П1) фрагмент ввода исходных данных был реализован следующим образом (здесь мы используем идентификатор man вместо идентификатора dbase):
int i = 0;
whi le (f i n. ge tl i ne (b u f . l_ bu f )) {
// . . .
st rn c py (ma n[ i]. na m e , bu f , l_ na me) :
8
ma n[i ]. na m e [ 1_ na me ] = ‘ \0’ ;
ma n[i ]. birt h _ ye a r = a to i(& b uf [l _ na me] ) :
ma n[i ]. pa y = a t o f( &b u f [ l_ na me + l _ yea r ]);
i++;
}
Как видно из этого фрагмента, в теле цикла whi le на i -й итерации выполняются следующие действия:
o очередная строка читается из файла fin в символьный массив bu f ;
o из массива b uf в поле name структуры ma n[ i ] копируются первые l _ na me символов;
o из буфера b u f извлекается очередная порция символов, отличных от пробела, каждый из которых преобразуется к типу i nt и записывается в поле b irt h _y ea r структуры ma n[ i] ;
o извлекается следующая порция символов, отличных от пробела, преобразуется к
типу fl oa t и записывается в поле pay структуры ma n [i] .
Если чисто механически скопировать такое разделение обязанностей между клиентом
(ma i n ) и сервером (объект класса Ma n ), то мы получим в теле цикла код вида:
ma n[ i] .S e t Na me (b u f) ;
ma n[ i] .S e tB irt hY e a r(a t oi( &b u f[l j ia me] )) ;
ma n[ i] .S e tPa y(a to f( &b u f [ lj ia me + l_ ye a r]) );
i+ + ;
Вряд ли такое решение можно признать удачным. Глаз сопровождающего программиста наверняка «споткнется» на аргументах вида a toi( &b u f [l _ na me] ) . Ведь подобные
выражения фактически являются сущностями типа «как делать», а не сущностями типа «что
делать»! Иными словами, крайне нежелательно нагружать клиента избыточной информацией. Второстепенные детали, или детали реализации, должны быть упрятаны (инкапсулированы) внутрь класса. Поэтому в данной задаче более удачной является сигнатура метода v oid
Set Bir th Yea r (c o ns t c h a r* f rom B u f) . Аналогичные размышления можно повторить и для метода SetP a y () .
Разобравшись с методами доступа, перейдем теперь к основной части алгоритма
функции ma in() , решающей подзадачу поиска сотрудника в базе по заданной фамилии и вывода сведений о нем.
Напомним, что в задаче 6.1 (П1) фрагмент поиска сотрудника с заданным именем
(name) был реализован с помощью следующего цикла:
fo r ( i = 0 ; i < n _ re co r d; + +i) {
i f ( str st r(ma n[ i]. na me , na me))
// 1
i f (ma n[ i] .na me [ strl e n ( na me) ] = =’ ’ ) {
// 2
st rc p y( na me , ma n [i] . na me ); // 3
c o ut < < na m e < < ma n[ i] .bi rt h _y ea r < <’ ’ < <ma n[ i]. pa y < <e nd l ;
// 4
// . . .
}
}
Опять задумаемся над вопросом: какая информация в этом решении избыточна для
клиента? Здесь решаются две подзадачи: сравнение имени в очередной прочитанной записи
с заданным именем name (операторы 1 и 2) и вывод информации в поток cout (операторы 3 и
4).
Поручим решение первой подзадачи методу Com pa re Na me ( c on st c ha r * na m e ) , инкапсулировав в него подробности решения, а решение второй подзадачи  методу P ri nt () .
9
Проектирование интерфейса класса Man завершено. Давайте посмотрим на текст программы (листинг 2.1), в которой реализован и использован описанный выше класс Man.
ПРИМЕЧАНИЕ -----------------------------------------------------------------------------------------------Мы будем придерживаться следующего стиля программирования:
Код каждого класса размещается в двух файлах: интерфейс  в заголовочном (.h ) , реа
лизация  в исходном (с рр ), поэтому все программы будут реализовываться как многофайловые проекты, в которых главный клиент ma i n() также занимает отдельный
файл.
Имена всех классов, а также имена методов всегда начинаются с прописной буквы,
имена полей (переменных)  со строчной буквы.
---------------------------------------------------------------------------------------------------------------Ли сти н г 2. 1 . Те к ст пр о гра ммы
/////// ///// ///// ///// /// /////// ///// ///// ///// /// /////// //
// П ро е к т Ta sk 2 _ 1
/////// ///// ///// ///// /// /////// //// / ///// ///// /// /////// //
// Ma n . h
#i n cl u de <i o stre a m >
// Ана л о г фа й ла i os tr ea m. h
#i n cl u de < cs tri n g >
// Ана л о г фа й ла stri n g. h
#i n cl u de "R us CO ut .h "
us i ng na me s pa ce std ;
// И сп оль з уем с та н да ртно е п ро ст ра н ств о и мен
co n st
co n st
co n st
co n st
in t
in t
int
in t
l _ na m e = 3 0 ;
l _ ye a r = 5 ;
l _ pa y = 1 0 ;
l _ bu f = l _ na me + l_ y ea r + l _pa y ;
cla ss Ma n {
pu bli c :
Ma n( in t l Na me = 3 0) ;
~ Ma n() ;
bo ol Co mpa r e Na m e (c o nst cha r *) c on st ;
i nt Ge t Bi rt hYe a r() c o nst { ret ur n bi rt h _y ea r; }
f loa t Ge t Pa y() c o ns t { re t ur n pa y ; }
c ha r* Ge t Na m e () c on st { ret ur n p Na me ; }
v oi d Pri n t() c o nst ;
v oi d Se tBi rt hYe a r( co n st c ha r *) ;
v oi d Se t Na me ( c o ns t c ha r* );
v oi d Se tPa y( co n st c ha r *) ;
pri va te :
c ha r* p Na me ;
i nt b irt h _ ye a r;
f loa t pa y ;
};
Ma n : : Ma n (i nt l Na me ) {
R u sC Ou t(o s < < " Co ns tr u cto r i s wor k in g " < < en dl) ;
p Na me = ne w cha r[ lNa me + 1 ] ;
}
Ma n : : ~ Ma n () {
R u sC Ou t(o s < < " De str u cto r is wo rk i ng " < < en dl) ;
de l e te [] p Na me ;
}
vo id Ma n : :S e t Na me (c o ns t c ha r * fr om B uf ) {
st rn c py (p Na m e , f ro mB uf , l _na me) ;
p Na me [l _ na me ] = 0 ;
}
vo id Ma n : :S e tB irt hY e a r(c o ns t ch a r * fr om B uf ) {
bi rt h_ y e a r = a t oi( fr omB u f + l _ na me );
}
10
vo id Ma n : :S e tPa y( c on s t c ha r* fr omB u f) {
pa y = a to f (f ro mB u f + l _ na me + l _ yea r) ;
}
bo ol Ma n :: Co mpa r e Na me ( co ns t c ha r * na me) c o nst {
i f ( ( st rst r(p Na me , n a me )) && (p Na me[ str l en( na m e)] = = ' ' ) )
r e t ur n t r ue ;
e l se
r e t ur n fa l se ;
}
vo id Ma n : :Pr in t() c o ns t {
R u sC Ou t(o s < < p N a me < < bir th _ yea r < < ' ' < < pa y < < e n dl) ;
}
/////// ///// ///// ///// /// /////// ///// ///// ///// ///
// Ma n . cpp
#i n cl u de < fs tre a m >
#i n cl u de "M a n . h"
co n st c ha r fil e na me [] = " dba se. txt " ;
int ma in ()
{
c o nst i nt ma xn _ re c ord = 1 0 ;
Ma n ma n[ma x n_ re c ord ];
c ha r b u f [l _b u f + 1 ];
c ha r na me [l _ na me + 1] ;
i fst re a m fi n( fi le na m e );
i f (! f in )
{
R us CO ut( os < < "Н е т фа й ла " < < fil en a me < < e n dl) ;
re t ur n 1 ;
}
i nt i = 0 ;
w hile ( fi n .ge tli ne (b u f, l _b u f))
{
i f (i > = ma x n _r e c ord )
{
R u sC Ou t(o s < < "С ли ш к о м дл инны й фа йл ") ;
re t ur n 1 ;
}
ma n[ i] .S e t Na me (bu f );
ma n[ i] .S e tB irt hY e a r(b u f) ;
ma n[ i] .S e tPa y( b u f) ;
i++;
}
i nt n _re c or d = i, n _ma n = 0;
f loa t m e a n _ pa y = 0 ;
w hile (t ru e )
{
R us CO ut ( os < < "Вв е д ите фа ми ли ю и л и с л ов о : ") ;
ci n > > n a me ;
i f ( 0 = = st rc mp( na me , "e n d" )) brea k ;
b o ol n ot _ fo u nd = tr ue ;
fo r ( i = 0 ; i < n _re c or d; + +i)
{
i f ( ma n[i] .C o mpa re Na me( na m e))
{
ma n[ i] .Pr i nt() ;
11
n _ma n + + ; me a n _pa y + = ma n[i ]. Get Pa y() ;
no t_ f o u nd = fa l se ;
b re a k ;
}
}
i f ( not _ f o un d) R us CO ut( os < < " Та ко г о с отр у дн ик а не т" < < end l) ;
}
i f ( n _ma n) R us CO ut (os < < " Сре д ний о к ла д : " < < m ea n_ pa y / n _ ma n < < e nd l) ;
re t ur n 0 ;
}
///// ///// ///// ///// //// /////// ///// ///// ///// /// /
Следует обратить внимание на следующие моменты.
Ввод/вывод кириллицы. В консольных приложениях (DOS-приложениях) при выводе текста на экран используется кодировка OEM, которая для русских букв отличается от
кодировки ANSI, используемой в операционных системах типа Windows. Поэтому если выводимый в программе русский текст набран в кодировке ANSI, то его экранный вывод становится нечитаемым.
Для разрешения указанной проблемы можно использовать один следующий прием:
перед экранным выводом выполнить преобразование выводимого на экран текста из кодировки ANSI в кодировку OEM. Сущность этого приема иллюстрируют листинги 2.2 и 2.3.
Ли сти н г 2. 2 . Фа й л R u sCO ut . h
/*
*/
Фа й л R u sCO ut . h.
Р у си фи ка ци я к он со ль но г о в ы в о да .
// П ре до тв ра щ е ние в оз мож но ст и мно г о кра тно г о п о д кл ю чени я
// да нн ог о фа й ла
#i f nd e f _ _R u sC Ou t_ h
#d ef i ne _ _R us CO ut _ h
# i nc l ude
# i nc l ude
# i nc l ude
# i nc l ude
<i ost re a m >
<i oma ni p >
<s stre a m >
<st ri ng >
//
//
//
//
П от о ков ы й в в о д -в ы в од
Ма нип у лят оры с па ра мет ра ми
Ст ро к ов ы е по то ки
Ст ро к и
u si ng na me spa ce st d;
// И сп оль з уем с та н да ртно е п ро ст ра н ств о и мен
# i nc l ude <w i nd ow s .h >
// Д ля C ha r T oOem ( )
// Ма кр оо пре де ле н ие с па ра м етр ом - в ы в од ит те кс т и з
// па ра м е тра Te x tF orO ut на э кра н с п р едв а рите ль ны м
// п ре о б ра зов а ни е м в D OS - к од ир ов ку ( OE M)
# de fi ne R usC O ut( Te xt Fo r O ut )
\
{
\
o str i ng stre a m os ;
\
Te xt Fo rO ut ;
\
s tri ng s = os .s tr( ) ;
\
cha r *p T e xt = ne w c ha r [ s .s ize( ) + 1 ];
\
i f( ! p Te xt )
\
{
\
c ou t < < " \ n R u sCO ut : er ro r 1 0 DM "
\
< < e n dl ;
\
e xi t( 1 0 );
\
}
\
:: C ha r T oOe m ( s. c _st r( ), p Te xt );
\
co ut << p Te xt < < e ndl ;
\
12
//
//
//
//
//
//
//
//
//
//
de le te p Te xt ;
\
}
По я сне н ия к ма к ро опре д еле ни ю с па р а метр о м:
os - об ъе кт в ы хо дн о го стр о ков о го п ото ка ;
Te xt Fo rO ut - п а ра ме тр с те к сто м , п од леж а щ и м в ы в о ду
на э к ра н ;
s - стр о ка с те к сто м , п о дле жа щ и м в ы в о ду на э кра н;
p T e xt - ук а за т е ль н а обла сть Д П с т ек ст ом ,
п о дл е ж а щ им в ы в о д у н а эк ра н (тип у ка за те ля
c ha r * );
C ha r To Oe m( ) - ф ун к ци я п рео бра зов а ния из A NS I в OE M
(s . c _s tr( ) - > p Te xt)
#e nd i f
Ли сти н г 2. 3 . Фа й л R u sCO ut . cpp
/*
*/
Фа йл R u sC Ou t . cp p.
Р ус иф ик а ция к он с оль н о г о в ы в о да с и с п оль зов а ние м
в к л юча е м о го фа йл а R u sC Ou t . h.
#i n cl u de "R us CO ut .h "
// Ру си фи ка ц ия э к ра нн ог о в ы в о да
// Та к р е ко ме н д уе т о фо рм лять за г ол ов ок г ла в н ой ф ун к ции
// с та н да рт я зы ка С + +, е сл и к ома н д на я с тро ка не
// и спо л ь зу е т ся
int ma in ( )
{
i nt
i = 1 2;
do u ble
d = 2 1 .2 1 ;
// Эк ра нны й в ы в о д д ля к он с оль н о г о п ри лож ения с
// п е ре к од ир ов ко й р у сс к о го те к ста в О Е М
// Вме с то тра ди ц и онн о го в ы в о да
// co ut < < e n dl < < "Р у с с кий те к ст " < < en dl << "i = "
//
< < se tw ( 1 0 ) < < i < < set pre ci si on ( 3 ) < <
//
", d = " < < d < < e nd l;
// и с по ль з уй те с ле ду ющ и й ма к ров ы з ов :
R us CO ut( o s < < e ndl < < "Р у с с кий те к ст " < < en dl < < "i = "
< < se tw ( 1 0 ) < < i < < setp re ci s io n( 3 ) < <
", d = " < < d < < e nd l ) ;
// Пр и и сп о ль з ов а нии да н но г о ма кр ов ы зов а с ле д ует
// со б л ю да ть с ле д у ющ ие пра в ила :
// 1. И де н тиф и ка тор об ъе кта э к ра нн о г о в ы в о да ( os )
//
я в ля е т ся фи к сир ов а н ны м .
// 2. П ра в и ла пот ок ов о го в ы в о да язы к а С + + в ча ст и
//
ма ни пу ля то р ов и ф ла г ов фо рма т и ров а н ия по лн ос ть ю
//
п ри ме ни мы и в да н но м сл у ча е
}
re tu r n 0 ;
Заголовочные файлы. Стандартная библиотека C++ имеет несколько реализаций. В
первоначальной версии библиотеки использовались заголовочные файлы с расширением . h ,
например <io str e a m .h > . Если вы работаете с компилятором, который поддерживает версию
библиотеки, вошедшую в стандарт языка ISO/IE C 1 4 8 8 2 ( 1 9 9 8) , то заголовочные файлы
можно указывать без расширения, например <i os trea m > . Кроме того, обычно используется
13
директива usi n g na me spa ce s td ; , так как все имена в стандартной версии библиотеки принадлежат пространству std . В этой книге все примеры программ даются в расчете на версию
библиотеки, соответствующую указанному стандарту, поскольку в старых версиях отсутствуют некоторые очень удобные средства, например не реализован класс str in g .
Константные методы. Обратите внимание, что заголовки тех методов класса, которые не должны изменять поля класса, снабжены модификатором const после списка параметров. Если вы по ошибке попытаетесь в теле метода что-либо присвоить полю класса,
компилятор не позволит вам это сделать. Другое достоинство ключевого слова c o ns t — оно
четко показывает сопровождающему программисту намерения разработчика программы.
Например, если обнаружено некорректное поведение приложения и выяснено, что «кто-то»
портит одно из полей объекта класса, то сопровождающий программист сразу может исключить из списка подозреваемых методы класса, объявленные как const. Поэтому использование const в объявлениях методов, не изменяющих объект, считается хорошим стилем программирования.
Отладочная печать в конструкторе и деструкторе. Вывод сообщений типа « Con str u cto r is w or ki ng» , « De str u ct or is w or ki ng» очень помогает на начальном этапе освоения
классов. Да и не только на начальном  мы сможем убедиться в этом, когда столкнемся с
проблемой локализации неочевидных ошибок в программе.
6.4.1 Отладка программы
С технологией создания многофайловых проектов вы уже знакомы по первой книге
практикума. Создайте проект с именем T a s k 1 _ 1 и добавьте к нему приведенные выше файлы. Не забудьте поместить в текущий каталог проекта файл с базой данных dba se. txt , заполнив его соответствующей информацией, например:
Ив а нов И . П.
Петр ов А. Б.
Си до ров С. С.
Ив а нов с кий Р .Е .
1 95 7
1 94 7
1 93 8
1 96 3
4 20 0
3 80 0
3 00 0
6 20 0
Скомпилируйте и запустите программу на выполнение. Вы должны увидеть следующий вывод в консольном окне программы:
Co ns tr uc to r i s w o r ki n g
Co ns tr uc to r i s w or ki ng
Co ns tr uc to r i s w or ki n g
Co ns tr uc to r i s w or ki ng
Co ns tr uc to r i s w or ki ng
Co ns tr uc to r i s w or ki n g
Co ns tr uc to r i s w or ki ng
Co ns tr uc to r i s w or ki ng
Co ns tr uc to r i s w or ki ng
Co ns tr uc to r i s w or ki n g
Вв е дите фа ми ли ю ил и с л ов о e nd :
Теперь вы можете оценить, насколько полезна отладочная печать в теле конструктора.
Мы четко видим, как создаются все 10 элементов массива ma n .
Продолжим тестирование программы. Введите с клавиатуры текст: « Си до ров » . После нажатия Enter вы должны увидеть на экране новый текст:
Си до ров С. С.
1 93 8
Вв е дите фа ми ли ю ил и с л ов о e nd :
3000
Введите с клавиатуры текст: « Петр ов » . После нажатия E nter появятся две строки:
Петр ов А. Б.
1 94 7
Вв е дите фа ми ли ю ил и с л ов о e nd :
3800
Введите с клавиатуры текст: « end» . После нажатия E nter появится вывод:
14
Сре дни й ок ла д : 3 4 00
De str u ct or is w o r ki ng
De str u ct or is w o r ki ng
De str u ct or is w o r ki ng
De str u ct or is w o r ki ng
De str u ct or is w o r ki ng
De str u ct or is w o r ki ng
De str u ct or is w o r ki ng
De str u ct or is w o r ki ng
De str u ct or is w o r ki ng
De str u ct or is w o r ki ng
Pres s a n y ke y to co nt i n ue
Все верно! После ввода end программа покидает цикл while (t r ue) и печатает величину среднего оклада для вызванных ранее записей из базы данных. Средний оклад рассчитан правильно. Затем программа завершается (re tu r n 0 ), а при выходе из области видимости, все объекты вызывают свои деструкторы, и мы это тоже четко наблюдаем.
ПРИМЕЧАНИЕ ----------------------------------------------------------------------------------------------------------В реальных программах доступ к полям класса с помощью методов преследует еще одну важную цель  проверку заносимых значений. В качестве самостоятельного упражнения дополните методы Se t Bir th Ye a r и Se tP a y проверками успешности преобразования из строки в число
и осмысленности получившегося результата (например, на неотрицательность).
---------------------------------------------------------------------------------------------------------------------------
Итак, мы завершили перестройку структурной программы в программу, реализующую концепции ООП.
ВНИМАНИЕ ---------------------------------------------------------------------------------------------------------------Если ваши последующие программы будут компилироваться в интегрированной среде на
платформе Wind ow s и в них возникнет потребность в потоковом вводе/выводе кириллицы, не
забудьте подключить к проекту два файла: CyrIO S . h и C yr IO S. с рр , которые находятся в папке La b 02 , и добавить директиву #i n cl ude « C y rIO S. h» в начало модулей, содержащих потоковый ввод/вывод.
---------------------------------------------------------------------------------------------------------------------------
Перейдем теперь к рассмотрению других аспектов, необходимых для создания хорошо спроектированного класса.
6.5 Инициализаторы конструктора
Существует альтернативный способ инициализации отдельных частей объекта в конструкторе — с помощью списка инициализаторов, расположенного после двоеточия между
заголовком и телом конструктора. Каждый инициализатор имеет вид имя _п ол я (выражение)
и обеспечивает инициализацию указанного поля объекта значением указанного выражения.
Если инициализаторов несколько, они разделяются запятыми. Если полем объекта является
объект другого класса, для его инициализации будет вызван соответствующий конструктор.
Например, для класса P oi nt , определяющего точку на плоскости в декартовой системе
координат:
cla ss P oi nt {
do ub le x, у;
pu bli c :
P oi nt (d ou ble _x = 0 , d o ub le j = 0 ) { x = x ; у = j; }
// . . .
};
конструктор можно записать в альтернативной форме:
Poi nt( do u ble _ х = 0 , d ou ble _ y = 0) : х (_ х) , у( _ у) {}
15
Обратите внимание, что тело конструктора пустое, а действия по присваиванию
начальных значений полям перенесены в инициализаторы конструктора. В этом примере
может быть выбран любой вариант конструктора. Однако есть три ситуации, в которых инициализация полей объекта возможна только с использованием инициализаторов конструктора:
o для полей, являющихся объектами класса, в котором есть один или более конструкторов, но отсутствует конструктор по умолчанию;
o для констант;
o для ссылок.
Обратите также внимание на то, что все параметры конструктора имеют значения по
умолчанию. Благодаря этому данный конструктор может использоваться в двух формах: как
конструктор с параметрами и как конструктор по умолчанию.
Вообще говоря, инициализация элементов класса с помощью списка инициализаторов
является более эффективной по быстродействию, чем присваивание начальных значений в
теле конструктора. Например, если объявлен объект Point р( 1. 5 , 2 . 0 ) , а в классе использован конструктор без списка инициализаторов, то при создании объекта р его поля х и у будут сначала инициализированы значением 0 (стандартное требование C++), а затем в теле
конструктора они получат значения 1.5 и 2.0. Если же в классе Point использован конструктор с инициализаторами, то при создании объекта р его полям х и у сразу будут присвоены значения 1.5 и 2.0.
6.6 Статические элементы класса
До сих пор одноименные поля разных объектов одного и того же класса были уникальными. Но что делать, если необходимо создать переменную, значение которой будет
общим для всех объектов конкретного класса? Если воспользоваться глобальной переменной, то это нарушит принцип инкапсуляции данных. Модификатор sta tic как раз и позволяет
объявить поле в классе, которое будет общим для всех экземпляров класса. Кроме объявления статического поля в классе, необходимо также дать его определение в глобальной области видимости программы, например:
cla ss C oo {
sta ti c i nt co u nt ; / / об ъ яв л ение в к ла с се
// о ста ль но й к од
};
int C oo : : co u nt = 1; // опре д еле ние и и ни ци а лиз а ция
// i nt C oo : :c o u nt ; // по у мо лч а ни ю ини циа лиз ир ует ся н у лем
Аналогично статическим полям могут быть объявлены и статические методы класса
(с модификатором sta ti c ). Они могут обращаться непосредственно только к статическим полям и вызывать только другие статические методы класса, потому что им не передается
скрытый указатель t hi s . Статические методы не могут быть константными (co n st ) и виртуальными (vi rt ua l ). Обращение к статическим методам производится так же, как к статическим полям  либо через имя класса, либо, если хотя бы один объект класса уже создан, через имя объекта.
Все рассмотренные выше аспекты найдут применение при решении второй задачи
этих методических указаний.
6.7 Задача 2.2. Реализация класса треугольников
Для некоторого множества заданных координатами своих вершин треугольников
найти треугольник максимальной площади (если максимальную площадь имеют несколько
треугольников, то найти первый из них). Предусмотреть возможность перемещения треугольников и проверки включения одного треугольника в другой.
16
Для реализации этой задачи составить, описание класса треугольников на плоскости.
Предусмотреть возможность объявления в клиентской программе (ma i n ) экземпляра треугольника с заданными координатами вершин. Предусмотреть наличие в классе методов,
обеспечивающих: 1) перемещение треугольников на плоскости; 2) определение отношения >
для пары заданных треугольников (мера сравнения  площадь треугольников); 3) определение отношения включения типа: «Треугольник 1 входит в (не входит в) «Треугольник 2».
Программа должна содержать меню, позволяющее осуществить проверку всех методов класса.
Отметим, что сейчас мы еще не готовы провести полномасштабную объектноориентированную декомпозицию программы. Для этого нам не хватает знаний, которые будут получены на втором семинаре. Поэтому применим гибридный подход: разработку главного клиента ma in() проведем по технологии функциональной декомпозиции, а функциисерверы, вызываемые из ma in( ) , будут использовать объекты.
Начнем с выявления основных понятий/классов для нашей задачи. Первый очевидный
класс Triangle необходим для представления треугольников. Из нескольких способов определения треугольника на плоскости выберем самый простой  через три точки, задающие его
вершины. Сделанный выбор опирается на новое понятие, отсутствующее в условии задачи, 
понятие точки. Точку на плоскости можно представить различными способами; остановимся
на наиболее популярном  с помощью пары вещественных чисел, задающих координаты
точки по осям х и у.
Таким образом, с понятием точки связывается как минимум пара атрибутов. В принципе, этого уже достаточно, чтобы подумать о создании класса Point. Если же представить,
что можно делать с объектом типа точки  например, перемещать ее на плоскости или определять ее вхождение в заданную фигуру,  то становится ясным, что такой класс Point будет полезен.
Итак, объектно-ориентированная декомпозиция дала нам два класса: T ria ng le и P oi nt .
Как эти два класса должны быть связаны друг с другом? На втором
семинаре мы будем более подробно рассматривать взаимоотношения между классами. Пока же отметим, что наиболее часто для двух классов имеет место одно из двух:
□ отношение наследования (отношение is а);
□ отношение агрегации или включения (отношение has а).
Если класс В является «частным случаем» класса А , то говорят, что В is а А (например,
класс треугольников есть частный вид класса многоугольников: Tria ng le is a Po l yg on ).
Если класс А содержит в себе объект класса В , то говорят, что A has а В (например,
класс треугольников может содержать в себе объекты класса точек: T r ia n gle has a P oi nt ).
Теперь уточним один вопрос: в каком порядке мы будем перечислять точки, задавая
треугольник? Порядок перечисления вершин важен для нас потому, что в дальнейшем, решая подзадачу определения отношения включения одного треугольника в другой, мы будем
рассматривать стороны треугольника как векторы. Условимся, что вершины треугольника
перечисляются в направлении по часовой стрелке.
Займемся теперь основным клиентом  ma in() . Здесь мы применяем функциональную
декомпозицию, или технологию нисходящего проектирования. В соответствии с данной технологией основной алгоритм представляется как последовательность нескольких подзадач.
Каждой подзадаче соответствует вызываемая серверная функция. На начальном этапе проектирования тела этих функций могут быть заполнены «заглушками»  отладочной печатью.
Если при этом в какой-то серверной функции окажется слабое сцепление, то она в свою очередь разбивается на несколько подзадач.
То же самое происходит и с классами, используемыми в программе: по мере реализации подзадач они пополняются необходимыми для этого методами. Такая технология
облегчает отладку и поиск ошибок, сокращая общее время разработки программы.
17
В соответствии с описанной технологией мы представим решение задачи 1.2 как последовательность нескольких этапов. Иногда как бы по забывчивости мы будем допускать
некоторые «ляпы», поскольку в процессе поиска ошибок можно глубже понять нюансы программирования с классами.
На первом этапе мы напишем код для начального представления классов Poi nt и Tr ia ng le , достаточный для того, чтобы создать несколько объектов типа Triangle и реализовать первый пункт меню — вывод всех объектов на экран.
6.7.1 Этап 1
/////// ///// ///// ///// /// /////// ///// ///// ///// /// /////// //
// П ро е к т Ta sk 2 _ 2
/////// ///// ///// ///// /// /////// ///// ///// ///// /// /////// //
// P oi nt .h
#i n cl u de "R us CO ut .h "
#i f nd e f PO I N T _H
#d ef i ne P OI N T _ H
cla ss P oi nt {
pu bli c :
// Ко нс тр у кт ор
Po i nt(d o ub le _ x = 0, d o ubl e _ y = 0) : x( _x ), y ( _y ) { }
// Др у гие ме то ды
v oi d S how () c on st ;
pu bli c :
do u ble x , y ;
};
vo id Po i nt :: S h ow () c o ns t {
R u sCO ut (o s < < " (" < < ", " < < y < < ") ") ;
}
#e nd i f /* PO I N T _H * /
/////// ///// ///// ///// /// /////// ///// ///// ///// ///
// Tr ia n gle . h
// Ре а ли за ция кла с са Tr ia ng l e
#i n cl u de < cma t h >
#i n cl u de <i o stre a m >
#i n cl u de <i oma n ip >
#i n cl u de < cs tri n g >
us i ng na me s pa ce std ;
//
//
//
//
//
Ана л о г фа й ла ma th .h
Ана л о г фа й ла i os tr ea m. h
Ана л о г фа й ла i oma ni p. h
Ана л о г фа й ла stri n g. h
И сп оль з уем с та н да ртно е п ро ст ра н ств о и мен
#i n cl u de "R us CO ut .h "
#i n cl u de "P oi nt . h"
#i f nd e f TR I A N GLE _ H
#d ef i ne TR IA N G LE _H
cla ss Tri a n gle {
pu bli c :
Tria n gle ( Po in t, Po i nt , P oi nt , c on st c ha r * ) ; // к он стр у кт ор
Tria n gle ( c o nst c ha r *) ; / / ко нс тр у кт ор п у ст о го (н ул ев о г о) тре у г оль н и ка
~ T ria n gle ( );
// д ес тр у кт ор
Po i nt Ge t _ v 1() co n st { re tu r n v 1; } // П о лу чи ть з на ч ение v1
Po i nt Ge t_ v 2() co n st { re tu r n v 2; } // П о лу чи ть з на ч ение v2
Po i nt Ge t_ v 3() co n st { re tu r n v 3; } // П о лу чи ть з на ч ение v3
18
c ha r* Ge t Na m e () c on st { ret ur n na me ; } // П о лу чи ть и мя об ъе кта
v oi d S h ow () c o ns t ;
/ / По ка за ть об ъе кт
v oi d S h ow Sid e A nd Are a () co n st ; // П ок а за ть ст ор оны и пл ощ а дь об ъе кта
pu bli c :
sta ti c i nt co u nt ;
// к о лич ес тв о с о зда нны х об ъе кт ов
pri va te :
c ha r * o b jI D ;
// и дент иф ик а то р о бъе кта
c ha r * na me ;
/ / на име нов а ние тр еу г оль н и ка
Po i nt v 1, v2 , v 3 ;
// в ерш ины
do u ble a ;
// ст ор она , со ед иня ю щ а я v 1 и v2
do u ble b ;
// ст ор она , со ед иня ю щ а я v 2 и v3
do u ble c ;
// ст ор она , со ед иня ю щ а я v 1 и v3
};
// К он ст ру к тор
Tr ia ngl e : : T ria ng le (P oi nt _ v 1, P oi nt _ v 2 , P oi nt _ v 3, co n st c ha r * id ent)
: v 1( _ v1 ), v 2 (_ v 2) , v 3( _ v3 ) {
c ha r b u f[ 1 6] ;
ob j I D = ne w ch a r[ s trle n( ide nt) + 1] ;
st rc p y(o b jI D , i de nt );
c o un t + +;
sp ri nt f( b uf , " Тр е уг оль н и к %d ", co u nt) ;
na me = n e w c ha r[ s trle n( bu f ) + 1] ;
st rc p y( na me , bu f );
a = sq rt(( v 1 .x - v 2 . x) * (v 1 .x - v 2. x) + ( v 1. y - v 2 . y) * ( v1 . y - v 2. y) ) ;
b = s qrt( (v 2 .x - v3 . x) * (v 2 .x - v 3. x) + ( v 2. y - v 3 . y) * ( v2 . y - v 3. y) ) ;
c = sqr t(( v 1. x - v 3. x) * (v 1 .x - v 3. x) + ( v 1. y - v 3 . y) * ( v1 . y - v 3. y) ) ;
c o ut < < " Co n str u ct or _ 1 fo r : " < < ob j I D
< < " ( " < < n a me < < ") " < < e nd l; / / от ла д очны й в ы в о д
}
// К он ст ру к тор п у ст о г о (н у лев о го) тр е уг о ль ника
Tr ia ng l e : : T ria ng le ( co n st c ha r * ide nt ) {
c ha r b u f[ 1 6] ;
ob j I D = ne w ch a r[ s trle n( ide nt) + 1] ;
st rc p y(o b jI D , i de nt );
}
c o un t + +;
sp ri nt f( b uf , " Тр е уг оль н и к %d ", co u nt) ;
na me = n e w c ha r[ s trle n( bu f ) + 1] ;
st rc p y( na me , bu f );
a = b = c = 0;
c o ut < < " Co n str u ct or _ 2 fo r : " < < ob j I D
< < " ( " < < n a me < < ") " < < e nd l; / / от ла д очны й в ы в о д
// Де ст р ук то р
Tr ia ngl e : : ~ T ria ng le () {
c o ut < < " De st ru ct o r fo r: " < < ob jI D < < e nd l;
de l e te [] ob j ID ;
de l e te [] na me ;
}
// П о ка за ть об ъе кт
vo id Tria n gle : : Sh ow ( ) co n st {
c o ut < < na me < < " : ";
v 1 . Sh ow ( ); v 2 .S h ow () ; v 3. S h ow() ;
c o ut < < e ndl ;
19
}
// П о ка за ть ст ор оны и п лощ а дь об ъе кта
vo id Tria n gle : : Sh ow Si de A ndA rea ( ) co n st {
do u ble p = (a + b + c) / 2 ;
do u ble s = s qrt (p * (p - a ) * (p - b) * (p - c)) ;
c o ut < < " - -- -- - -- -- - -- -- - -- -- - -- - -- -- - -- -- - -- - -- -- - -- - " < < en dl ;
c o ut < < na me < < " : ";
c o ut. pre c is io n( 4) ;
c o ut < < " a = " < < se tw ( 5) < < a ;
c o ut < < " , b = " < < se t w( 5) < < b ;
c o ut < < " , c = " < < se t w ( 5) < < c ;
c o ut < < " ;\ ts = " < < s < < e ndl ;
}
#e nd i f / * TR IA N G LE _H */
/////// ///// ///// ///// /// /////// ///// ///// ///// /// /////// //
// Ma i n. h
// Ре а ли за ция ф ун к ц и й для го л ов но г о мо д у ля
// - - -- -- - -- -- - в в о д ц е л о го чи с ла в за да н но м д иа па з оне
int Ge t N um be r( i nt mi n , i nt ma x) {
i nt n um be r = m in - 1 ;
bo ol s u c ce s s = fa l s e ;
w hile (! su c ce ss) {
ci n > > n u mbe r;
i f (( n umb e r > = mi n) && ( nu mbe r < = ma x) & & ( ci n .pe ek () = = ' \ n' ) )
bre a k ;
e lse {
R u sC Ou t (o s < < " Пов то рите в в о д ( ожи да ет с я чи сл о от " < < mi n
<< " д о " < < ma x < < ") : " < < en dl) ;
c i n. cle a r() ;
w hile ( ci n .ge t() ! = ' \n' ) { };
}
}
re t ur n n u mbe r;
}
// - - -- -- - -- -- - -- - -- -- - -- - -- -- - - - - в ы в о д мен ю
int M e n u( ) {
R u sC Ou t (o s < < " \n = = = = = Г л а в н о е м е н ю = = = = = " < < en dl) ;
R u sC Ou t (o s < < "1 - в ы в е ст и в се об ъе кты \ t 3 - на й ти ма к сим а ль ны й" < < en dl) ;
R u sC Ou t (o s < < "2 - пе ре ме стить \t\t 4 - о пре де лить от нош ение в к л юче ния "
< < e n dl );
R u sC Ou t(o s < < " \t\ t
5 - в ы хо д " < < e nd l);
re t ur n Ge t N um be r ( 1, 5) ;
}
// - - -- -- - -- -- - -- - в озв р а т в ф ун к ци ю с о сн ов ны м мен ю
vo id E x itBa c k() {
R u sC Ou t(o s < < " На ж мит е E nte r. " < < e nd l);
c in .g e t() ; ci n. ge t( ) ;
}
// - - -- -- - -- -- - -- - -- -- - -- - -- -- в ы в од в сех тр еу г оль н и ков
vo id S h ow ( T ria ng le * p _tr ia [] , in t k) {
20
R u sC Ou t (o s < < " = = = = = = = Пер ече нь т р еу г оль н и ков = == = = = = = " < < en dl) ;
f or (i nt i = 0; i < k ; + +i ) p _ tria [i] - > Sh ow ();
f or (i = 0 ; i < k ; + +i ) p _t ria [ i] - >S h ow Si d eAn dAr ea () ;
E x itBa c k() ;
}
// - - -- -- - -- -- - -- - -- -- - -- - -- -- - -- -- - - п ере мещ е н ие
vo id M o ve ( Tr ia ngl e * p _tr ia [] , in t k) {
R u sC Ou t (o s < < " = = = = = Пер еме щ ени е = = = = = " < < en dl) ;
// з де сь б у де т ко д фу н к ции .. .
E x itBa c k() ;
}
// - - -- -- - -- -- - -- - -- -- - п ои с к м а к си ма ль н ог о т реу г о ль ни ка
vo id F i nd Ma x ( T ria ng le * p_ tria [] , i nt k ) {
R u sC Ou t (o s < < " = = = П ои ск ма к си ма ль н о го тре у г оль ни ка = = " < < e ndl ) ;
// з де сь б у де т ко д фу н к ции .. .
E x itBa c k() ;
}
// - - -- -- - -- -- - -- - -- -- - - о пре де ление о тн ош ени я в кл ю чени я
vo id I sI n cl ude d ( Tr ia ng le * p _t ria [ ], i nt k) {
R u sC Ou t (o s < < " = = = = = Отн ош ен ие в к л юч ения = = = = = " < < e nd l) ;
// з де сь б у де т ко д фу н к ции .. .
E x itBa c k() ;
}
/////// ///// ///// ///// /// /////// ///// ///// ///// /// /////// //
// Ma i n. cp p
// Г о лов н а я пр ог ра м м а
#i n cl u de
#i n cl u de
#i n cl u de
#i n cl u de
"P oi nt . h"
" Tr ia ng le . h "
"R us CO ut .h "
"M a i n. h "
// И ни ц иа л иза ц ия г ло б а ль ны х п ере мен ны х
int Tr ia n gle : : co u nt = 0 ;
// - - -- -- - -- -- - -- - -- -- - -- - - - -- - - г ла в на я ф ун к ц и я
int ma in () {
// Оп ре де ле ни я т оч е к
Po i nt p1 (0 , 0) ;
P oi nt
Po i nt p3 (1 , 0) ;
P oi nt
Po i nt p5 (2 , 1) ;
P oi nt
Po i nt p7 (2 , 2) ;
P oi nt
p 2( 0. 5 , 1) ;
p 4( 0, 4 . 5) ;
p 6( 2, 0 );
p 8( 3, 0 );
// Оп ре де ле ни я т ре у г оль н и ков
Tria n gle tri a A (p 1 , p 2, p 3 , "tr ia A ") ;
Tria n gle tri a B( p1 , p 4, p 8 , "tr ia B ") ;
Tria n gle tri a C( p1 , p 5, p 6 , "t r ia C ") ;
Tria n gle tri a D (p 1, p7 , p8 , "t ria D ") ;
// Оп ре де ле ни е ма с си в а у ка за те лей на тре у го ль н ик и
Tria n gle * p Tri a [] = { & tr ia A , & tria B , & tri a C , &tr ia D };
i nt n = s ize o f ( p Tri a ) / si zeo f (p T ria [ 0] );
// Г ла в ны й ци к л
21
bo ol d o ne = fa ls e ;
w hile (! d o ne ) {
sw it c h (Me n u() ) {
ca s e 1 : S how (p T ria , n) ;
b rea k ;
ca s e 2 : Mo ve ( p T ria , n) ;
b rea k ;
ca s e 3 : Fi nd Ma x (p Tria , n) ;
bre a k ;
ca s e 4 : I sI n cl u d e d(p T ria , n) ; b rea k ;
ca s e 5 : R us CO ut (os < < "К оне ц ра б от ы ." < < e n dl) ;
d o ne = tr ue ;
brea k ;
}
}
re t ur n 0 ;
}
// - - -- -- - -- -- - -- - -- к оне ц пр ое кта Ta s k 2_ 2
/////// ///// ///// ///// /// /////// ///// ///// ///// //
Рекомендуем вам обратить внимание на следующие моменты в проекте Task1_2.
o Класс P oi nt (файл Poi nt .h ).
 Реализация класса Point пока что содержит единственный метод S ho w () , назначение которого очевидно: показать объект типа Poi nt на экране. Здесь следует
заметить, что при решении реальных задач в какой-либо графической оболочке
метод S ho w () действительно нарисовал бы нашу точку, да еще в цвете. Но мы-то
изучаем «чистый» C++, так что придется удовольствоваться текстовым выводом
на экран основных атрибутов точки — ее координат.
o Класс Tr ia n gle (файл T ria ngl e. h ).
 Назначение большинства полей и методов очевидно из их имен и комментариев.
 Поле s ta ti c i nt count играет роль глобального счетчика создаваемых объектов;
мы сочли удобным в конструкторах генерировать имена треугольников автоматически: «Треугольник 1», «Треугольник 2» и т. д., используя текущее значение
count (возможны и другие способы именования треугольников).
 Поле c ha r * o bj I D избыточно для решения нашей задачи  оно введено исключительно для целей отладки и обучения; вскоре вы увидите, что благодаря отладочным операторам печати в конструкторах и деструкторе удобно наблюдать за
созданием и уничтожением объектов.
 Метод Sh ow Si de A nd Ar ea () введен также только для целей отладки,  убедившись, что стороны треугольника и его площадь вычисляются правильно (с
помощью калькулятора), в дальнейшем этот метод можно удалить.
 Конструктор пустого (нулевого) треугольника предусмотрен для создания временных объектов, которые могут модифицироваться с помощью присваивания.
 Метод Sh ow ( )  см. комментарий выше по поводу метода Sh ow ( ) в классе
Poi nt . К сожалению, здесь нам тоже не удастся нарисовать треугольник на
экране; вместо этого печатаются координаты его вершин.
o Основной модуль (файлы Ma i n. h , Ma i n .с рр ).
 Инициализация глобальных переменных: обратите внимание на оператор i nt
Tr ia ngl e : : c ou nt = 0 ;  если вы забудете это написать, компилятор очень сильно
обидится.
 Функция ma in() :
22
определения восьми точек p1, . .. , р 8 выбраны произвольно, но так, чтобы из
них можно было составить треугольники;
определения четырех треугольников сделаны тоже произвольно, впоследствии
на них будут демонстрироваться основные методы класса; однако не забывайте,
что вершины в каждом треугольнике должны перечисляться по часовой стрелке;
далее определяются массив указателей Tr ia ng le * p Tria [] с адресами объявленных выше треугольников и его размер n ; в таком виде удобно передавать адрес p T riа и величину n в вызываемые серверные функции;
главный цикл функции довольно прозрачен и дополнительных пояснений не
требует.
 Функция Me n u()  после вывода на экран списка пунктов меню вызывается
функция Ge t N um be r () , возвращающая номер пункта, введенный пользователем
с клавиатуры. Для чего написана такая сложная функция  Get Nu m ber() ? Ведь
можно было просто написать: cin > > nu mbe r ; ? Но тогда мы не обеспечили бы
защиту программы от непреднамеренных ошибок при вводе. Там же вы можете
найти описание работы аналогичной функции Get I nt() .
 Функция Sh ow () npocтo выводит на экран перечень всех треугольников. В завершение вызывается функция E xitBa c k() , которая обеспечивает заключительный диалог с пользователем после обработки очередного пункта меню.
 Остальные функции по обработке оставшихся пунктов меню выполнены в виде
заглушек, выводящих только наименование соответствующего пункта.
6.7.1.1 Тестирование и отладка первой версии программы
После компиляции и запуска программы вы должны увидеть на экране следующий
текст:
Co ns tr uc to r_ 1 fo r :
Co ns tr uc to r_ 1 fo r :
Co ns tr uc to r_ 1 fo r :
Co ns tr uc to r_ 1 fo r :
t ria A
t ria B
t ria C
t ria D
(Т ре у го ль ни к
(Т ре у го ль ни к
(Т ре у го ль ни к
(Т ре у го ль ни к
1)
2)
3)
4)
= = = = = = = = = = = = Гла в ное м е н ю = = = = = = = = = = = =
1 - в ы в е ст и в се об ъе кт ы 3 - на йт и ма к с има ль ны й
2 - пе ре ме стить
4 - опр е дел ить отн ош ение в к л юче ния
5 - выход
Введите с клавиатуры цифру 1 . Программа выведет:
1
= = = = = = Пе р е че нь т р е у г оль н и к ов = == = = =
Тр еу г о ль ни к 1 : (0 , 0) (0 . 5, 1) ( 1 , 0)
Тр еу г о ль ни к 2 : (0 , 0) (0 . 4 . 5) ( 3 , 0)
Тр еу г о ль ни к 3 : (0 , 0) (2 , 1) ( 2, 0)
Тр еу г о ль ни к 4 : (0 . 0) (2 . 2) ( 3, 0)
-- -- - -- -- - -- - -- -- - Тр еу г о ль ни к 1 : а = 1 . 1 18 , b = 1 . 1 18 , с = 1 : s = 0 . 5
-- -- - -- -- - -- - -- -- - Тр еу г о ль ни к 2 : а = 4. 5 , Ь = 5. 4 0 8, с = 3 : s = 6 . 7 5
-- -- - -- -- - -- - -- -- - Тр еу г о ль ни к 3 : а = 2 . 2 36 , Ь = 1, с = 2 : s = 1
-- -- - -- -- - -- - -- -- - Тр еу г о ль ни к 4 : а = 2 . 8 28 , Ь = 2. 2 3 6, с = 3 : s = 3
На ж м ите E n te r .
Выбор первого пункта меню проверен. Нажмите E nte r . Программа выведет:
23
= = = = = = = = = = = = Гла в ное м е н ю = = = = = = = = = = = =
Теперь проверим выбор второго пункта меню. Введите с клавиатуры цифру 2. На
экране должно появиться:
2
= = = = = = = = = = = = = = = = = = = = = Пе р е ме щ е ние = = = = = = = = = = = = = = = = = = = = =
На ж м ите E n te r .
Выбор второго пункта проверен. Нажмите Enter. Программа выведет:
= = = = = = = = = = = = Гла в ное м е н ю = = = = = = = = = = = =
Теперь проверим ввод ошибочного символа. Введите с клавиатуры любой буквенный
символ, например w, и нажмите Enter. Программа должна выругаться:
Пов т ор ите в в о д (ож и да е т ся ч и сл о от 1 д о 5) :
Проверяем завершение работы. Введите цифру 5 . Программа выведет:
5
Кон е ц ра б оты .
De str u ct or f or :
De str u ct or f or :
De str u ct or f or :
De str u ct or f or :
tria D
tria C
tria B
tria A
Тестирование закончено. Обратите внимание на то, что деструкторы объектов вызываются в порядке, обратном вызову конструкторов.
Продолжим разработку программы. На втором этапе мы добавим в классы P oi nt и
Tr ia ngl e методы, обеспечивающие перемещение треугольников, а в основной модуль — реализацию функции Mo ve ( ) . Кроме этого, в классе T ria ng l e мы удалим метод
S how Si deA n dAre a () , поскольку он был введен только для целей отладки и свою роль уже
выполнил.
6.7.2 Этап 2
Внесите следующие изменения в тексты модулей проекта.
1) Модуль Po in t. h :
 добавьте
сразу
после
объявления
метода
S ho w()
объявление
операции-функции «+ = », которая позволит нам впоследствии реализовать метод
перемещения M ov e () в классе T ria ng le :
vo id op e ra t or + =( Poi nt &) ;
 добавьте код реализации данной функции:
vo id Po i nt :: op e ra t or + =( Po i nt& p) {
x + = p .x ;
у += р.у;
}
2) Модуль T ria ng le .h .
 удалите объявление метода S how Si deA n dAre a () ;
 добавьте объявление метода:
vo id M o ve (P oi nt );
 удалите реализацию метода S how Si deA n dAre a () ;
 добавьте реализацию метода M ov e() :
// Пе ре ме стить о б ъе кт на в е ли чин у (d p .x . dp. у )
vo id Tria n gle : :M o v e (Po in t d p) {
vl + = d p ; v 2 + = d p ; v 3 + = dp ; } .
24
3) Модуль Ma i n . h .
 добавьте в файл текст новой функции GetD ou ble () либо сразу после функции
S how () , либо в конец файла. Эта функция предназначена для ввода вещественного числа и вызывается из функции Mo ve() . В ней предусмотрена защита от ввода недопустимых (например, буквенных) символов аналогично тому,
как это решено в функции GetN u mber () :
// - -- -- - -- в в о д в е щ е ств е нн ог о чи с ла
do u ble Ge t Do u ble ( ) {
d o ub le va l ue ;
w hi le (tr ue ) {
ci n > > va l ue ;
if ( ci n. pe e k( ) = = ‘ \ n’ ) brea k ;
e lse {
c o ut < < " Пов т ор ите в в о д (ож ид а ет ся в ещ еств е нн ое чи сл о) : "
< < e nd l ;
c in . cle a r() ;
w hile ( ci n .get () ! = ‘ \n’ ) {} ;
}
}
r e t ur n va l ue ;
}
 замените заглушку функции Move () следующим кодом:
// - -- - -- - - пе ре ме щ е ние
vo id M ov e ( Tr ia ngle * p_ tria [] , i nt k ) {
c o ut < < " = = = = = = Пер еме щ ени е = = = = = = " < < en dl ;
c o ut < < " Вв е ди т е н оме р т ре у го ль н ик а (о т 1 д о " < < k
< < ") : " ;
i nt i = Ge t Nu m be r( 1 ,k ) - 1 ;
p _tr ia [i ] - > S how ();
Po in t d p ;
c o ut < < " Вв е ди те сме щ ение п о х : ";
dp .x = Ge t D o ub le () :
c o ut < < " Вв е ди те сме щ ение п о у : " ;
dp . у = Ge t Do ub le () ;
p _tr ia [i ] - > Mo ve (dp) ;
c o ut < < " Нов о е по л ожен ие т ре у го л ь ник а : " < < e nd! ;
p _tr ia [i ] - > S how ();
E xi tBa c k() ;
}
Выполнив компиляцию проекта, проведите его тестирование аналогично тестированию на первом этапе. После выбора второго пункта меню и ввода данных,
задающих номер треугольника, величину сдвига по х и величину сдвига по у, вы
должны увидеть на экране примерно следующее:
2
= = = = = = Пе ре ме щ е ние = = = = = =
Вв е д ите но ме р т р е у г оль н и ка ( от 1 до 4 ): 1
Тре у г оль ни к 1 : ( 0 , 0) (0 . 5, 1) ( 1 , 0)
Вв е д ите с ме щ е ни е по х : 2 . 5
Вв е д ите с ме щ е ни е по у: - 7
Нов ое по ло ж е ни е тре у го ль ни ка :
Тре у г оль ни к 1 : ( 2 .5 , - 7) (3 , - 6) ( 3. 5, - 7 )
На ж м ите E n te r .
Решение задачи 2.2 завершено.
25
Давайте повторим наиболее важные моменты данной лабораторной работы.
1. Использование классов лежит в основе объектно-ориентированной декомпозиции
программных систем, которая является более эффективным средством борьбы со
сложностью систем, чем функциональная декомпозиция.
2. В результате декомпозиции система разделяется на компоненты (модули, функции,
классы). Чтобы обеспечить высокое качество проекта, его способность к модификациям, удобство сопровождения, необходимо учитывать сцепление внутри компонента (оно должно быть сильным) и связанность между компонентами (она
должна быть слабой), а также правильно распределять обязанности между компонентом-клиентом и компонентом-сервером.
3. Класс  это определяемый пользователем тип, лежащий в основе ООП. Класс содержит ряд полей (переменных), а также методов (функций), имеющих доступ к
этим полям.
4. Доступ к отдельным частям класса регулируется с помощью ключевых слов: pub li c
(открытая часть), pri va te (закрытая часть) и protected (защищенная часть). Последний вид доступа имеет значение только при наследовании классов. Методы, расположенные в открытой части, формируют интерфейс класса и могут вызываться через соответствующий объект.
7 Задания
Вариант 1
Описать класс, реализующий стек. Написать программу, использующую этот класс
для моделирования Т-образного сортировочного узла на железной дороге. Программа должна разделять на два направления состав, состоящий из вагонов двух типов (на каждое
направление формируется состав из вагонов одного типа). Предусмотреть возможность формирования состава из файла и с клавиатуры.
Вариант 2
Описать класс, реализующий бинарное дерево, обладающее возможностью добавления новых элементов, удаления существующих, поиска элемента по ключу, а также последовательного доступа ко всем элементам.
Написать программу, использующую этот класс для представления англо-русского
словаря.
Программа
должна
содержать
меню,
позволяющее
осуществить
проверку всех методов класса. Предусмотреть возможность формирования словаря из файла
и с клавиатуры.
Вариант 3
Создать класс Money для работы с денежными суммами. Число должно быть представлено двумя полями: типа long для рублей и типа unsigned char  для копеек. Дробная
часть (копейки) при выводе на экран должна быть отделена от целой части запятой. Реализовать сложение, вычитание, деление сумм, деление суммы на дробное число, умножение на
дробное число и операции сравнения.
Вариант 4
Построить описание класса, содержащего информацию о почтовом адресе организации. Предусмотреть возможность раздельного изменения составных частей адреса, создания и уничтожения объектов этого класса.
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
26
Вариант 5
Составить описание класса для представления комплексных чисел. Обеспечить выполнение операций сложения, вычитания и умножения комплексных чисел.
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
Вариант 6
Составить описание класса для объектов-векторов, задаваемых координатами
концов в трехмерном пространстве. Обеспечить операции сложения и вычита
ния векторов с получением нового вектора (суммы или разности), вычисления
скалярного произведения двух векторов, длины вектора, косинуса угла между
векторами.
^
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
Вариант 7
Составить описание класса прямоугольников со сторонами, параллельными осям координат. Предусмотреть возможность перемещения прямоугольников на плоскости, изменение размеров, построение наименьшего прямоугольника, содержащего два заданных прямоугольника, и прямоугольника, являющегося общей частью (пересечением) двух прямоугольников.
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
Вариант 8
Составить описание класса для определения одномерных массивов целых чисел (векторов). Предусмотреть возможность обращения к отдельному элементу массива с контролем
выхода за пределы массива, возможность задания произвольных границ индексов при создании объекта, возможность выполнения операций поэлементного сложения и вычитания массивов с одинаковыми границами индексов, умножения и деления всех элементов массива на
скаляр, вывода на экран элемента массива по заданному индексу, вывода на экран,всего массива.
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
Вариант 9
Составить описание класса для определения одномерных массивов строк фиксированной длины. Предусмотреть возможность обращения к отдельным строкам массива по
индексам, контроль выхода за пределы массива, выполнения операций поэлементного сцепления двух массивов с образованием нового массива, слияния двух массивов с исключением
повторяющихся элементов, вывод на экран элемента массива по заданному индексу и всего
массива.
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
Вариант 10
Составить описание класса многочленов от одной переменной, задаваемых степенью
многочлена и массивом коэффициентов. Предусмотреть методы для вычисления значения
многочлена для заданного аргумента, операции сложения, вычитания и умножения многочленов с получением нового объекта-многочлена, вывод на экран описания многочлена.
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
27
Вариант 11
Составить описание класса одномерных массивов строк, каждая строка задается длиной и указателем на выделенную для нее память. Предусмотреть возможность обращения к
отдельным строкам массива по индексам, контроль выхода за пределы массивов, выполнения операций поэлементного сцепления двух массивов с образованием нового массива, слияния двух массивов с исключением повторяющихся элементов, вывод на экран элемента
массива и всего массива.
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
Вариант 12
Составить описание класса, обеспечивающего представление матрицы произвольного
размера с возможностью изменения числа строк и столбцов, вывода на экран подматрицы
любого размера и всей матрицы.
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
Вариант 13
Написать класс для эффективной работы со строками, позволяющий форматировать и
сравнивать строки, хранить в строках числовые значения и извлекать их. Для этого необходимо реализовать:
 перегруженные операции присваивания и конкатенации;
 операции сравнения и приведения типов;
 преобразование в число любого типа;
 форматный вывод строки.
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
Вариант 14
Описать класс «домашняя библиотека». Предусмотреть возможность работы с произвольным числом книг, поиска книги по какому-либо признаку (например, по автору или по
году издания), добавления книг в библиотеку, удаления книг из нее, сортировки книг по разным полям.
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
Вариант 15
Описать класс «записная книжка». Предусмотреть возможность работы с произвольным числом записей, поиска записи по какому-либо признаку (например, по фамилии,
дате рождения или номеру телефона), добавления и удаления записей, сортировки по разным
полям.
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
Вариант 16
Описать класс «студенческая группа». Предусмотреть возможность работы с переменным числом студентов, поиска студента по какому-либо признаку (например, по фамилии, дате рождения или номеру телефона), добавления и удаления записей, сортировки по
разным полям.
28
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
Вариант 17
Описать класс, реализующий тип данных «вещественная матрица» и работу с ними.
Класс должен реализовывать следующие операции над матрицами:
 сложение, вычитание, умножение, деление (+, -, *, /) (умножение и деление, как на
другую матрицу, так и на число);
 комбинированные операции присваивания (+=, -=, *=, /=);
 операции сравнения на равенство/неравенство;
 операции вычисления обратной и транспонированной матрицы, операцию возведения в степень;
 методы вычисления детерминанта и нормы;
 методы, реализующие проверку типа матрицы (квадратная, диагональная, нулевая,
единичная, симметрическая, верхняя треугольная, нижняя треугольная);
 операции ввода/вывода в стандартные потоки.
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
Вариант 18
Описать класс «множество», позволяющий выполнять основные операции — добавление и удаление элемента, пересечение, объединение и разность множеств.
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
Вариант 19
Описать класс, реализующий стек. Написать программу, использующую этот класс
для отыскания прохода по лабиринту.
Лабиринт представляется в виде матрицы, состоящей из квадратов. Каждый квадрат
либо открыт, либо закрыт. Вход в закрытый квадрат запрещен. Если квадрат открыт, то вход
в него возможен со стороны, но не с угла. Каждый квадрат определяется его координатами в
матрице. После отыскания прохода программа печатает найденный путь в виде координат
квадратов.
Вариант 20
Описать класс «предметный указатель». Каждый компонент указателя содержит слово и номера страниц, на которых это слово встречается. Количество номеров страниц, относящихся к одному слову, от одного до десяти. Предусмотреть возможность формирования
указателя с клавиатуры и из файла, вывода указателя, вывода номеров страниц для заданного
слова, удаления элемента из указателя.
Написать программу, демонстрирующую работу с этим классом. Программа должна
содержать меню, позволяющее осуществить проверку всех методов класса.
Download