Вы находитесь на странице: 1из 120

Министерство инфраструктуры Украины

Государственная служба связи

Одесская национальная академия связи им. А.С.Попова


________________________________________________________________________________________________________________________

Кафедра информационных технологий

В.А. Шаповаленко, И.Г. Швайко

Технологии программирования

МОДУЛЬ № 1

Часть 2

Объектно-ориентированное программирование

Методические указания
к лабораторным и практическим занятиям

для студентов направлений бакалаврской подготовки:


“Телекоммуникации”,
“Автоматизация и компьютерно-интегрированные технологии”

Одесса 2011
2
УДК 004.43 План УМИ 2011 г.

Методические указания разработали: В.А. Шаповаленко, И.Г. Швайко

Методические указания содержат краткие теоретические сведения об ос-


новных понятиях и принципах объектно-ориентированного программирования.
В соответствии с учебным планом дисциплины «Технологии программирова-
ния» в методических указаниях также приведены индивидуальные задания для
выполнения лабораторных работ и комплексного задания. Для приобретения
практических навыков в пособии приведено много примеров программ на алго-
ритмическом языке C++ для решения задач, использующих принципы объект-
но-ориентированного программирования. Методические указания будут полез-
ны студентам для закрепления лекционного материала, а также при подготовке
к практическим занятиям и выполнению лабораторных работ.
Методические указания предназначены для приобретения теоретических и
практических знаний студентами академии, которые изучают дисциплины
“Технологии программирования” и “Компьютерно-интегрированные техноло-
гии”. Это пособие будет также полезным аспирантам и научным сотрудникам,
которым необходимо решать научные и технические задачи с использованием
объектно-ориентированного программирования.

ОДОБРЕНО
на заседании кафедры
информационных технологий
и рекомендовано к печати
Протокол № 7
от 1 февраля 2011 г.

УТВЕРДЖЕНО
методическим советом
академии связи
Протокол № 8
от 11.02.2011
3

ПРЕДИСЛОВИЕ
Технология программирования играла разную роль на разных этапах раз-
вития программирования. По мере повышения мощности компьютеров и разви-
тия средств и методологии программирования росла и сложность решаемых на
компьютерах задач, что привело к повышенному вниманию к технологии про-
граммирования. Резкое удешевление стоимости компьютеров и, в особенности,
стоимости хранения информации на компьютерных носителях привело к широ-
кому внедрению компьютеров практически во все сферы человеческой дея-
тельности, что существенно изменило направленность технологии программи-
рования. Человеческий фактор стал играть в ней решающую роль. Сформиро-
валось достаточно глубокое понятие качества программного средства, причем
предпочтение стало отдаваться не столько его эффективности, сколько удобст-
ву работы с ним для пользователей (не говоря уже о его надежности). Широкое
использование компьютерных сетей привело к интенсивному развитию распре-
деленных вычислений, дистанционного доступа к информации и электронного
способа обмена сообщениями между людьми. Компьютерная техника из сред-
ства решения отдельных задач все более превращается в средство информаци-
онного моделирования реального и мыслимого мира, способное просто отве-
чать людям на интересующие их вопросы. Начинается этап глубокой и полной
информатизации (компьютеризации) человеческого общества. Все это ставит
перед технологией программирования новые и достаточно трудные проблемы.

Дисциплина "Технологии программирования" изучается в семестрах 3.1 и


3.2 студентами 3 курса, обучающихся по направлению бакалаврской подготов-
ки «Телекоммуникации». Для изучения дисциплины необходимы знания по та-
ким дисциплинам: "Информатика", "Вычислительная техника и микропроцес-
соры", "Организация и сохранение баз данных", "Инженерная и компьютерная
графика", "Теория электрических цепей и сигналов", "Технические и про-
граммные средства информатизации ". Знания по дисциплине являются исход-
ными к дисциплинам "Телекоммуникационные и информационные сети", "За-
щита информации в телекоммуникационных системах", "Основы оптимизации
систем и сетей" и т.д.
Цель изучения дисциплины − формирование базовых знаний по современ-
ным технологиям программирования и умений, необходимых для проектирова-
ния и работы с динамическими структурами данных, создания современных
программных продуктов с использованием объектно-ориентированного про-
граммирования и решения различных инженерных задач для отрасли связи с
помощью компьютера.
Программа курса состоит из двух модулей:
Модуль 1 – Объектно-ориентированное программирование.
Модуль 2 – Технологии программирования обмена данных.
4
Структура модуля 1
Программой модуля 1 дисциплины "Технологии программирования" пре-
дусмотрены лекции, практические занятия, лабораторный практикум и выпол-
нение комплексного задания.

Темы модуля
1. Операции с динамическими структурами данных. Проблемы и на-
правления развития технологий программирования. Динамические структуры
данных: стеки, очереди, списки, деревья. Программирование операций с дина-
мическими структурами данных.
2. Объектно-ориентированный подход в программировании. Модуль-
ное и объектно-ориентированное программирование. Понятие класса и объекта.
Поля и методы классов. Спецификаторы доступа к методам класса.
3. Построение классов и их потомков. Инкапсуляция. Наследования
классов. Конструкторы и способы их назначения. Деструкторы.
4. Полиморфизм. Перекрытие методов. Статические, виртуальные и дина-
мические методы. Абстрактные классы.
5. Классы и объекты библиотеки визуальных компонент. Иерархия
классов компонент системы программирования Borland С++. Использование
компонент-полей для визуализации объекта. Объекты-потомки классов
TComponent и TControl. Разработка собственных компонентов.

В этой, второй, части методических указаний для модуля 1 дисциплины


«Технологии программирования» рассматриваются только темы, посвященные
объектно-ориентированному программированию (темы 2−5). Темы построения
графических изображений и программирования динамических структур данных
рассмотрены в первой части методического пособия.

Перечень лабораторных работ по объектно-ориентированному програм-


мированию:
2.1 − «Программирование создания классов и объектов»;
2.2 − «Программирование создания классов с иерархической структурой»;
2.3 − «Программная реализация свойства полиморфизм в объектно-ориен-
тированном программировании».
5

1 ЭЛЕМЕНТЫ ОБЪЕКТНО-ОРИЕНТИРОВАННОГО
ПРОГРАММИРОВАНИЯ

1.1 Модульное и объектно-ориентированное программирование

В окончательном виде каждая программа является набором инструкций


для процессора, и чем выше является уровень языка, тем в более простой форме
записываются одни и те же действия. С увеличением объема программы необ-
ходимо структурировать информацию, выделять в ней главное и отбрасывать
несущественное. Этот процесс называется повышением степени абстракции
программы.
Первым шагом к повышению абстракции является использование функций
(подпрограмм), что позволяет после написания и отладки функции отвлечься от
деталей ее реализации, поскольку для вызова функции нужно знать только ее
интерфейс.
Следующий шаг - объявление собственных типов данных, которые позво-
ляют структурировать и группировать информацию, подавая ее в более естест-
венном виде.
Поскольку для работы с собственными типами данных требуются специаль-
ные функции, то считается естественным группировать их вместе с объявлением
этих типов в одном месте программы и определенным образом отделить от ос-
тальных ее частей. Следовательно, объединение в модуле объявлений типов дан-
ных и функций, предназначенных для работы с этими типами, вместе с сокрыти-
ем от пользователя модуля несущественных деталей, является дальнейшим раз-
витием структуризации программы. Такой метод разработки программ называют
модульным программированием. Программный модуль − это фрагмент описания
процесса, оформляемый как самостоятельный программный продукт, пригодный
для использования в описаниях процесса. Это означает, что каждый программ-
ный модуль программируется, компилируется и отлаживается отдельно от дру-
гих модулей программы, и тем самым, физически разделен с другими модулями
программы. Более того, каждый разработанный программный модуль может
включаться в состав разных программ, если выполнены условия его использо-
вания, декларированные в документации по этому модулю.
Все три упомянутых выше метода повышения абстракции имеют целью
упростить структуру программы, т.е. представить ее в виде небольшого коли-
чества более мощных модулей и минимизировать связи между ними. Это по-
зволяет управлять большим объемом информации и, следовательно, успешно
отлаживать большие программы.
Введение понятия класса является развитием идей модульности. В классе
сочетаются структуры данных и функции их обработки. Классы очень похожи
на тип данных − структуры в языке программирования С++ (или тип данных −
записи в языке Pascal). Идея классов является основой объектно-
ориентированного программирования (ООП). Программы на C++ широко ис-
пользуют классы.
6
Класс является типом данных, который определяет пользователь. В классе
задаются свойства в виде полей данных (аналогично структурам) и характер
определенного предмета или процесса в виде функций для работы с ними. На-
пример, класс телефон может иметь поля данных: номер, тип телефона (кно-
почный или дисковый), фирма-изготовитель и функции работы с телефоном:
звонок, набор номера, соединение с абонентом т.д. Объединение данных об
объекте и кодирование их одной переменной упрощает программирование и
увеличивает возможность повторного использования кода.
Существенным свойством класса является то, что детали его реализации
скрыты от пользователей класса за интерфейсом. Это защищает их от случай-
ных изменений. Интерфейсом класса являются заголовки его методов.
Идея классов отражает строение объектов реального мира. Ведь каждый
объект или процесс обладает набором определенных характеристик или разли-
чий, иначе говоря, определенными свойствами и поведением. Так характери-
стиками автомобиля является марка, цвет, максимальная скорость, мощность
двигателя и т.п.; характеристиками усилителя является частотный диапазон,
мощность, коэффициент нелинейных преобразований т.д. Функциональные
возможности объектов тоже отличаются: автомобиль может ездить, усилитель -
повышать уровень сигналов. А пользоваться объектами можно, не зная их
внутреннего строения. Например, управлять автомобилем можно, не имея ни-
какого представления о строении его двигателя или любых других его частей.
Каждый класс занимает определенное место в иерархии классов, например,
все автомобили принадлежат к классу наземный транспорт (более высокого в
иерархии), в свою очередь класс автомобили содержит много разновидностей
автомобилей: грузовые, легковые, микроавтобусы и т.д. Следовательно, любой
класс определяет некоторую категорию объектов, а каждый объект является
экземпляром некоторого класса. Объектно-ориентированное программирова-
ние − это методика, которая концентрирует основное внимание программиста
на связях между объектами, а не на деталях их реализации.
Основными понятиями, на которых базируется ООП, являются:
- инкапсуляция;
- наследования;
- полиморфизм.
Инкапсуляцией (encapsulation) называется сочетание данных с функциями
их обработки вместе с сокрытием лишней для пользования этими данными
информации. Когда коды и данные, объединены таким способом, создается
объект. Другими словами, объект – это то, что поддерживает инкапсуляцию.
Инкапсуляция повышает уровень абстракции программы, позво-ляет изменять
реализацию класса без модификации основной части программы и использо-
вать класс в другом окружении.
Наследование (inheritance) – это возможность создания иерархии классов,
когда классы-потомки наследуют свойства своих базовых классов (предков),
могут изменять эти свойства и приобретать новые. Свойства базовых классов
(предков) при наследовании не описываются, что сокращает объем программы.
7
Полиморфизм (polymorphism) – это возможность использовать в разных
классах иерархии одно имя для обозначения близких по смыслу действий и
гибко выбирать соответствующие действия в течении выполнения программы.
Итак, объектно-ориентированное программирование ни в коем случае не
связано с процессом выполнения программы, а является лишь новым способом
ее организации, то есть новой парадигмой программирования (парадигма – на-
бор теорий и методов, которые определяют способ организации знаний).
Существует ошибочное мнение, что объектно-ориентированное програм-
мирование является чем-то сложным и непонятным. Но объектная декомпози-
ция является не менее естественной и интуитивно понятной, чем алгоритмиче-
ская, которая господствовала до появления ООП. В программирование основ-
ные понятия ООП перешли из других областей знаний, таких как философия,
логика и математика, причем, без особых изменений, по крайней мере относи-
тельно существенности этих понятий. Объектный способ декомпозиции (пред-
ставления) является естественным, и используется на протяжении многих ве-
ков. Поэтому неудивительно, что в процессе эволюции технологии программи-
рования этот способ занимает достойное место и поддерживается так или иначе
практически всеми современными алгоритмическими языками.
Существует три группы языков программирования, которые связаны с по-
нятием класс: объектно-ориентированные, объектные, объектно-базированные.
Объектно-ориентированные языки в полном объеме поддерживают парадигму
ООП: инкапсуляцию, наследование и полиморфизм. Типичными представите-
лями таких языков является C + +, Java, C #. К объектным языкам относят язы-
ки программирования, которые поддерживают только инкапсуляцию и позво-
ляют создавать объекты; это языки Visual Basic (до шестой версии включитель-
но) и Ada. К объектно-базированным языкам программирования относят язы-
ки, которые могут использовать существующие объекты, но не имеют меха-
низма создания объектов пользователя. Язык JavaScript является объектно-
базированным языком программирования. Например, с помощью этого языка
можно использовать многочисленные элементы объектной модели документа
(DOM - Document Object Model), с помощью которых можно предоставлять со-
держание веб-страницы.
Первый объектно-ориентированный язык программирования Simula 67
был разработан в конце 60-х годов в Норвегии. Авторы этого языка очень точно
угадали перспективы развития программирования: их язык намного опередил
свое время. Однако современники (программисты 60-х годов) оказались не го-
товы воспринять ценности языка Simula 67, и он не выдержал конкуренции с
другими языками программирования (прежде всего, с языком Fortran). Про-
хладному отношению к языку Simula 67 способствовало и то обстоятельство,
что он был реализован как интерпретируемый (а не компилируемый) язык, что
было совершенно неприемлемым в 60-е годы, так как интерпретация связана со
снижением эффективности (скорости выполнения) программ.
Но достоинства языка Simula 67 были замечены некоторыми программи-
стами, и в 70-е годы было разработано большое число экспериментальных объ-
ектно-ориентированных языков программирования: например, языки CLU,
8
Alphard, Concurrent Pascal и др. Эти языки так и остались экспериментальными,
но в результате их исследования были разработаны современные объектно-
ориентированные языки программирования: C++, Smalltalk, Eiffel и др.
Наиболее распространенным объектно-ориентированным языком про-
граммирования безусловно является C++. Свободно распространяемые ком-
мерческие системы программирования C++ существуют практически на любой
платформе. Широко известна свободно распространяемая система программи-
рования G++, которая дает возможность всем желающим разобрать достаточно
хорошо и подробно прокомментированный исходный текст одного из образцо-
вых компиляторов языка C++. Завершена работа по стандартизации языка C++
(он доступен по Internet).
С 1995 года стал широко распространяться новый объектно-
ориентированный язык программирования Java, ориентированный на сети ком-
пьютеров и, прежде всего, на Internet. Синтаксис этого языка напоминает син-
таксис языка C++, однако эти языки имеют мало общего. Java интерпретируе-
мый язык: для него определены внутреннее представление (bytecode) и интер-
претатор этого представления, которые уже сейчас реализованы на большинст-
ве платформ. Интерпретатор упрощает отладку программ, написанных на языке
Java, обеспечивает их переносимость на новые платформы и адаптируемость к
новым окружениям. Эти свойства языка Java позволяют использовать его как
основной язык программирования для программ, распространяемых по сетям (в
частности, по сети Internet).
Разработка новых объектно-ориентированных языков программирования
продолжается. Так, в 1998 − 2001 годах группой инженеров под руководством
Андерса Хейлсберга в компании Microsoft разработан язык программирования
C# как основной язык разработки приложений для платформы Microsoft .NET.

Преимущества объектно-ориентированного подхода в программировании:


− уменьшение сложности программного обеспечения;
− повышение надежности программного обеспечения;
− обеспечение возможности модификации отдельных компонентов про-
граммного обеспечения без изменения остальных его компонентов;
− обеспечение возможности повторного использования отдельных компо-
нентов программного обеспечения;
− обеспечение возможности создавать расширяемые системы. Расширяе-
мость означает, что существующую систему можно заставить работать с новы-
ми компонентами, причем без внесения в нее каких-либо изменений. Компо-
ненты могут быть добавлены на этапе исполнения программы.
Систематическое применение объектно-ориентированного подхода позво-
ляет разрабатывать хорошо структурированные, надежные в эксплуатации, дос-
таточно просто модифицируемые программные системы.
9

1.2 Описание класса

Класс является абстрактным типом данных, определяемым пользователем;


он отображает модель реального мира в виде данных и функций их обработки.
Данные класса называют полями или данными-членами (data members), а функ-
ции класса − методами или функциями-членами (methods, member
functions).Поля и методы называются элементами класса.
Как правило, спецификация класса состоит из двух частей:
- объявление класса – в нем прописаны все поля и методы (элементы дан-
ных) класса на уровне интерфейса в терминах функций-элементов;
- определение методов класса, т.е. реализации конкретных функций-членов
данного класса.
Объявление класса имеет такой формат:
class <имя класса>
{ [ private: ]
< Объявление скрытых элементов класса >
protected:
< Объявление элементов, доступных только потомкам >
public:
< Объявление общедоступных элементов >
}; // Объявление класса завершается точкой с запятой

Спецификаторы доступа private, protected, public – управляют види-


мостью элементов класса, то есть возможностью использовать данные и зна-
чения функций класса в программе. По умолчанию, все функции и перемен-
ные, объявленные без спецификации, становятся закрытими для класса (спе-
цификатор доступа − private).
Рассмотрим пример:
class myclass {
private:int a; // закрытый элемент (поле класса)
public: // открытые эементы:
void set_a(int num) // метод присваивания значения num переменной a
{ a=num; }
int get_a()// метод получения значения переменной a
{ return a; }
};

Описанный класс имеет одну закрытую переменную целого типа a, и


две открытые функции, set_a и get_a(). Описания кодов функций располо-
жены внутри класса. Так как а является закрытой переменной, то она является
недоступной для любой функции вне класса myclass. Однако, поскольку set_a
и get_a() являются членами класса myclass, они имееют доступ к а. Более то-
10
го, set_a и get_a(), являясь открытыми членами этого класса, могут вызы-
ваться из любой части программы, использующей myclass.
Тела обоих методов (коды функций) содержатся непосредственно в объяв-
лении класса. Здесь реализация функций не означает, что код функции разме-
щается в памяти. Это произойдет лишь при создании объекта класса. Методы
класса, коды которых описаны внутри класса, по умолчанию являются встро-
енными методами или функциями класса.
При увеличении методов по количеству и объему описание функций клас-
са как встроенных может вызвать беспорядок в объявлении класса. Чтобы из-
бежать этого, функции внутри класса зачастую не описываются, а записывают-
ся только их заголовки (т.е. размещаются их прототипы), а само описание
функций происходит в другом месте. Например, объявление класса myclass без
встроенных функций-членов класса в этом случае будет иметь вид:
class myclass {
int a; // закрытый элемент
public: // открытые эементы:
void set_a(int num); // метод присваивания значения num переменной a
int get_a();// метод получения значения переменной a
};
Функция, описанная вне класса, по умолчанию уже не является встроенной.
Для описания метода (функции-члена) вне класса, мы должны связать имя
класса с именем функции, что достигается следующей конструкцией:
<тип функции> <имя класса>::<имя функции-члена класса>
{ тело метода − операторы }
Два двоеточия (::) называются оператором расширения области видимо-
сти. Например, описание вне класса функций класса myclass будет иметь вид:
void myclass::set_a(int num)
{ a=num; }
int myclass::get_a()
{ return a; }
Функции (методы), описанные таким способом можно расположить в дру-
гом месте (или после объявления класса, или даже в другом файле).

Пример 1.1 Создадим класс student (студент), который состоит из полей


данных (name – фамилия, group – группа, birthday – год рождения) и описаний
методов класса – функциями вне класса (show_student() – вывода информации
о студенте и year() − вычисления количества лет студенту в 2011 году):
// Объявление класса
class student
{ private: // закрытые поля класса
AnsiString name;
AnsiString group;
11
int birthday;
public: // общедоступные методы класса
AnsiString show_student (void);
int year();
}; // Объявление класса завершается точкой с запятой

// Реализация методов вне класса:


// Функция вывода информации о студенте
AnsiString student:: show_student (void)
{ AnsiString s = ”студент ” + name + ”, группа ” + group
+ ”, год рождения ” + IntToStr(birthday);
return s;}
//Функция вычисления количества лет студенту в 2011 году
int student::year()
{ return 2011− birthday;}

1.3 Создание объектов класса

После объявления класса и описания методов класса можно создавать кон-


кретные переменные этого класса, которые называют экземплярами класса или
объектами. Время их существования и видимость объектов зависит от формы
и места их объявления в соответствии с общими правилами языка С++. На са-
мом деле объект в ООП − это переменная, тип которой объявляет програм-
мист. Каждый элемент данных такого типа является составной переменной.
Например, создадим два объекта класса myclass:
myclass ob1, ob2, *ob3; //это объекты типа myclass
После того как объект класса создан можно обращаться к открытым эле-
ментам класса используя оператор точку (.) для переменных ob1 и ob2. Напри-
мер, для класса myclass можно записать операторы:
ob1.set_a(10);
ob2.set_a(99);
Здесь доступ к элементам объектов ob1 и ob2 (в данном случае к методам)
аналогичен доступу к полям структуры. Для этого после имени объекта (ob1)
поставлена точка (.), а после нее указывается имя метода set_a(). Таким обра-
зом, точка связывает метод с именем объекта. Следует помнить, что это прави-
ло справедливо только для доступа к статическим объектам класса. В приве-
денном примере оператор ob1.set_a(10) вызывает метод set_a(int num) для
объекта ob1, который присваивает полю a объекта ob1 значение 10. Оператор
ob2.set_a (99) присваивает полю a объекта ob2 значение 99.
Операторы
int x1 = ob1.get_a();
int x2 = ob2.get_a();
12
вызывают метод get_a () для объектов ob1 и ob2 и присваивают переменным
x1 и x2 значения полей соответствующих объектов, возвращенных этим мето-
дом. Как видим, скобки после имени метода являются обязательными, даже
если у функции нет параметров. Скобки свидельствует о том, что осуществля-
ется вызов функции, а не используется значение переменной. Теперь можно
вывести полученные данные об объектах ob1 и ob2, например, в компоненты
Edit с помощью операторов:
Edit1->Text = "В объекте ob1 поле a =" + IntToStr(x1);
Edit2->Text = "В объекте ob2 поле a =" + IntToStr(x2);

Для динамической переменной (объекта) *ob3 необходимо предварительно


выделить память, а уже потом использовать операцию доступа к элементам
класса. Доступ к динамическому объекту класса осуществляется с помощью
операции-стрелка (->), например:
myclass * ob3 = new myclass; / / выделение памяти для объекта
ob3->set_a(46); / / Полю a объекта *ob3 присвоено значение 46
Edit3->Text = "В объекте ob3 поле a =" +
IntToStr (ob3.get_a ());
Итак, результат выполнения рассмотренных выше операторов вывода в
компоненты Edit будет иметь вид:
В объекте ob1 поле a = 10
В объекте ob2 поле a = 99
В объекте ob3 поле a = 46
Примечания:
1. Обратиться с помощью операции точка (.) или операции (->) можно
лишь к элементам со спецификатором public.
2. Получить доступ или изменить значения элементов со спецификато-
рами private или protected можно только при вызове соответствующих мето-
дов. Если к закрытому методу элемента класса обратиться непосредственно че-
рез объект, а не через методы класса, то такое действие приведет к ошибке. На-
пример, инструкция ob1.a = 10; повлечет ошибку, если поле a объявлено в
разделе private класса, а этот элемент класса объявить в разделе доступа
public, то тогда к переменной a можно будет обращаться из любой части про-
граммы, например, так: ob1.a = 10;
3. Операция (.) для выбора метода работает в точности так же, как опе-
рация (->), за исключением того, что имени объекта предшествует неявно сге-
нерированный компилятором оператор адреса (&). Итак, оператор
ob1.set_a(10);
компилятор трактует как
(&ob1)->set_a(10);
13
1.4 Использование общедоступных и закрытых элементов класса

Рассмотрим программу, в которой используется описание класса student


(см. пример 1.1), который состоит из трех полей данных (name, group,
birthday) и двух методов show_student()и year(). Все элементы класса объ-
явим как общедоступные. В программе создадим два объекта для класса stu-
dent, присвоим значения элементам данных и с помощью функции
show_student (void)выведем информацию о студентах:
// Объявление класса
class student
{ public: // общедоступные поля и методы класса
AnsiString name;
AnsiString group;
int birthday;
AnsiString show_student (void);
int year();
};

// Реализация методов вне класса:


// Функция вывода информации о студенте
AnsiString student:: show_student (void)
{ AnsiString s = ”студент ” + name + ”, группа ” + group
+ ”, год рождения ” + IntToStr(birthday);
return s;}
//Функция вычисления количества лет студенту в 2011 году
int student::year()
{ return 2011− birthday;}

// Программа создания и управления объектами класса


// отклик на кнопку ”Информация о студентах”
void __fastcall TForm1::Button1Click(TObject *Sender)
{ student bacalavr, magistr; // объявление объектов класса student
// присваивание значений полям объекта bacalavr
bacalavr.name = " Петренко ";
bacalavr. group = "TC-4.01";
bacalavr. birthday = 1989;
// присваивание значений полям объекта magistr
magistr.name = " Марков ";
magistr. group = " РС-5.02";
magistr. birthday = 1987;
Memo1->Clear();
//строка информации об объекте bacalavr
AnsiString sb=bacalavr.show_student();
// вычисление количества лет для объекта bacalavr
int yb=bacalavr.year();
// вывод информации и результатов вычислений для объекта bacalavr
14
Memo1->Lines->Add(sb+", "+IntToStr(yb)+" года");
//строка информации об объекте magistr
AnsiString sm=magistr.show_student();
// вычисление количества лет для объекта magistr
int ym=magistr.year();
// вывод информации и результатов вычислений для объекта magistr
Memo1->Lines->Add(sm+", "+IntToStr(ym)+" года");
}

Как видим, в главной программе объявлены два объекта класса student −


bacalavr и magistr, для которых использована операция-точка при присвое-
нии значений полям объектов и при обращении к функциям show_student() и
year() для вывода результатов. Результаты выполнения программы:

Обратите еще раз внимание на то, что в этом примере все элементы класса
являются общедоступными, что предоставило возможность обращаться ко всем
элементам класса непосредственно через объекты bacalavr и magistr (на-
пример: bacalavr.name, magistr.show_student(), magistr.year()).
При создании класса можно иметь элементы, значения которых использу-
ются только в классе, но обращаться к которым в самой программе нет необхо-
димости. Такие элементы являются закрытыми (private), их следует скрывать
от главной (вызывающей) программы. По умолчанию в C++ считается, что все
элементы класса являются закрытыми. Обычно надо защищать элементы класса
от прямого доступа к ним, объявляя для них доступ private. Только при таком
доступе можно гарантировать пользователю программного продукта, что эле-
ментам класса будут присвоены допустимые значения.
Например, в приведенном выше примере программы, объекты bacalavr и
magistr используют поле birthday, которое может принимать значения толь-
ко в диапазоне от 1946 до 1996 (год рождения студента). Если элемент birth-
day есть общедоступный (как в примере), то программа может напрямую об-
ращаться к этому элементу, изменяя его значение без ограничений:
bacalavr. birthday = 1300;
magistr. birthday = 2150;
15
Если объявить поле birthday закрытым, то для присваивания ему значе-
ний нужно создать и использовать дополнительный метод класса. Например,
функция assign_birthday() может проверять, является ли значение поля до-
пустимым:

class student
{ private: // закрытое поле класса
int birthday;
public: // общедоступные поля и методы класса
AnsiString name;
........
int assign_birthday (int y); //функция проверки допустимости значений
........ };
// реализация функции проверки допустимости значений
int student::assign_birthday(int y)
{if ((y > = 1946) && (y <= 1996))
{ birthday = y; return (0);} // Успешное присваивание значения года
else return (-1); // Недопустимое значение
}
Методы класса, которые управляют доступом к элементам данных, − это
интерфейсные функции. При создании классов желательно использовать эти
интерфейсные функции для защиты данных.
Вышерассмотренные примеры демонстрируют способы использования ме-
тодов класса для инициализации полей (присваивания значений) объекта клас-
са. Удобной является инициализация поля объекта в момент его создания, а не
явный вызов в программе соответствующего метода (set_a(int num), assign_
birthday (int y)). Такую инициализацию данных можно реализовать с помо-
щью специального метода класса, который называется конструктором.

1.5 Конструкторы класса

Как было показано выше, для классов удобнее инициализировать поля


объекта класса (особенно для закрытых элементов класса) автоматически с по-
мощью специального метода, называемого конструктором класса. Конструк-
тор (constructor) – это метод, включаемый в описание класса и выполняющий-
ся автоматически в момент создания объекта. Он может не только инициали-
зировать переменные класса, но и выполнять другие задачи инициализации,
необходимые для подготовки объекта к использованию. Конструктор связыва-
ет имя класса с его закрытыми для доступа полями по формату:
<имя класса> (<переменная поля1>, <переменная поля2>, . . .)
Например, объявление класса student с конструктором может иметь вид:
16
class student / / Объявление имени класса
{ private: // закрытые поля класса
AnsiString name;
AnsiString group;
int birthday;
public: // общедоступные методы класса
student (AnsiString iname, AnsiString igroup, int ibirthday); // Конструктор
AnsiString show_student (void); //Функция информации о студенте
int year();//Функция вычисления количества лет студенту в 2011 году
};
В этом конструкторе будут инициализироваться переменные (поля)
класса из раздела доступа private: name, igroup, ibirthday. Чтобы отличать
соответствующую переменную от подобного имени поля класса, в объявлении
конструктора перед именами полей класса были добавлены буквы "i" (от слова
initialisation): iname, igroup, ibirthday.
Для создания экземпляра класса необходимо, чтобы конструктор был
объявлен как общедоступный (public). При объявлении объекта класса значе-
ния его полей передаются конструктору по формату:
< имя класса > <имя об’єкта>(<значение поля1>,< значение поля2>, . . .);
Например, используя объявления конструктора класса student создадим
объект bacalavr:
student bacalavr("Петренко","TC-4.01",1989) ;
Использование конструктора в приведенном примере уменьшило количе-
ство операторов инициализации полей объекта класса, причем поля данных яв-
ляются защищёнными от общего доступа.
Рассмотрим основные свойства конструктора.
− Конструктор выполняется автоматически в момент создания объекта,
чем упрощает задачу инициализации данных объекта.
− Имя конструктора абсолютно совпадает с именем класса. Итак,
компилятор отличает конструктор от других методов класса.
− Конструктор не возвращает никакого значения, даже типа void. Нель-
зя получить и указатель (адрес) на конструктор. Это объясняется тем, что кон-
структор автоматически вызывается системой, а следовательно, не существует
программы или функции, которая его вызывает, и которой конструктор мог бы
вернуть значение. Итак, задавать значение, которое возвращается, для конст-
руктора нет смысла.
− Класс может иметь несколько конструкторов с разными параметра-
ми для разных видов инициализации, при этом используется механизм пере-
грузки.
− Конструктор может принять какое угодно количество аргументов
(включая нулевое).
17

− Параметры конструктора могут быть какого угодно типа, кроме


этого класса. Можно задавать значения параметров по умолчанию (только в од-
ном из конструкторов объявленного класса).
− Конструкторы не наследуются.
− Конструкторы нельзя объявлять с модификаторами const, virtual и
static.
− Уничтожение объекта из памяти компьютера выполняет специальная
функция − деструктор.
Приведем пример конструктора в объявлении класса myclass (см.п.1.3):
class myclass
{private:
int a;
public:
myclass(int ia); // начальное значение a задается как параметр конструктора
set_a(int a);
int get_a();
};

В этом случае при создании объекта значение поля a инициализируется па-


раметром. Например:
// создание статического объекта со значеним a = 99
myclass ob2(99);
// создание динамического объекта со значеним a = 46
myclass *ob3=new myclass(46);
Объявим класс myclass с конструктором без параметра:
class myclass
{private:
int a;
public:
myclass(void) {a=0;} // конструктор класса без параметров
// начальное значение поля а равно 0
set_a(int a);
int get_a();
};
Формально, в конструкторе может быть любое количество параметров, по-
этому в классе могут использоваться разные конструкторы. С созданием и ис-
пользованием разных конструкторов будем знакомиться на практических при-
мерах. Конструкторы, так же как и обычные методы, можно перегружать, т.е.
использовать разные конструкторы для создания объектов одного класса.
Обычно различают четыре вида конструкторов:
- конструктор присваивания,
- конструктор со списком инициализации,
- конструктор по умолчанию,
18
- конструктор копирования (с одним параметром − именем класса).

Рассмотрим различные способы определения конструкторов.


1. Конструктор присваивания может инициализировать поля класса, ис-
пользуя оператор присвоения (=) по формату:
<имя класса> (<переменная поля1>, <переменная поля2>, ...)
{<имя поля1> = <переменная поля1>; <имя поля2> = переменная поля2>; . . }
Например, определим конструктор с операторами присвоения для класса
student (как встроенную функцію!):
student (AnsiString iname, AnsiString igroup, int ibirthday)
{name = iname; group = igroup; birthday = ibirthday;}
В этом случае для создания объекта (magistr2) достаточно вызвать кон-
структор, которому передаются соответствующие значения параметров:
student magistr2 ("Марченко", "PT-6.02",1990);
При этом способе определения конструктора легко реализовать проверку
условия на допустимость значений. Например, запишем конструктор, который
при создании объекта всегда будет проверять, принадлежит значение поля
birthday диапазону от 1946 до 1996:
student (AnsiString iname, AnsiString igroup, int ibirthday)
{name = iname; group = igroup;
if ((ibirthday > = 1946) && (ibirthday <= 1996))
birthday = ibirthday; // успешное присваивание
else birthday = 0;}; // если значение является недопустимым, присваиваем 0
}
Кроме того, при таком способе создания экземпляра класса с указателем,
значения параметров можно передавать конструктору, используя оператор new.
Например, создадим динамический объект * spec:
student * spec = new student ("Зименко", "ТКС-5.01", 1994);

2. Конструктор со списком инициализации. Конструктор присвоения не


может быть использован при инициализации полей-констант или полей-ссылок,
т. к. им не могут быть присвоены значения. Для этого предусмотрено специ-
альное свойство конструктора, называемое списком инициализации, который
позволяет инициализировать одну или более переменных, а не присваивать им
значения. Список инициализации располагается между прототипом метода и
его телом и предварен двоеточием. Инициализирующее значение помещается в
скобках после имени поля. Значения в списке разделяются запятыми. Формат
конструктора со списком инициализации:
<прототип конструктора>: <имя поля1>(<значение поля1>),
<имя поля2>(< значение поля2>), . . . { операторы. . .}
19
Конструктор со списком инициализации для класса student имеет вид:
student (AnsiString iname, AnsiString igroup, int ibirthday):
name ( iname), group ( igroup), birthday ( ibirthday) { }
В качестве значений поля можно также использовать константы:
student (AnsiString iname, int ibirthday):
name ( iname), birthday ( ibirthday), group ("ТКС-5.01") { }
Этот конструктор имеет в списке два переменных поля и одно константное
значение (group ="ТКС-5.01"). Создание объекта (например, с5) по такому
конструктору иметь вид:
student с5 ("Соловей", 1987);
3. Конструктор по умолчанию − это конструктор, у котрого в списке ини-
циализации все значения − константы.
<имя класса>(): <имя поля1>(<константа поля1>),
<имя поля2>(< константа поля2>), . . . { операторы. . .}
Такой конструктор, у которого все значения полей заданы в виде констант
называют также конструктором без параметров. Например,
student(): name ("Марченко"), group("ТКС-5.01"), birthday(1985) { }
Для создания объектов с использованием такого конструктора достаточно
записать имя класса (без пустых скобок) и имена создаваемых объектов через
запятую, например:
student с1, с2, с3;
Здесь все созданные объекты (с1, с2, с3) будут иметь одинаковые на-
чальные значения.
Если конструктор для любого класса не определен, то компилятор сам
генерирует конструктор по умолчанию. Такие конструкторы не присваивают
начальные значения полям класса. Поэтому если необходимо однозначно ини-
циализировать поля, нужно определить собственный конструктор (им может
быть и конструктор по умолчанию).
Если конструктор объявлен без аргументов, то такой конструктор вызы-
вается автоматически в том месте, где объявлен объект класса. При наличии
хотя бы одного явно определенного конструктора, конструктор без аргументов
не создается.
4. Конструктор копирования при инициализации объекта использует
значения полей уже существующего (созданного ранее) объекта. Такой конст-
руктор не надо самим создавать, он предоставляется компилятором для каждо-
го создаваемого класса и называется конструктором копирования по умолча-
нию. Он может иметь только один аргумент, который является объектом того
же класса.
20
Например, создадим три объекта stud1, stud2, stud3 различными спосо-
бами:
student stud1 ("Серов", "КТ-4.11", 5000.00); // Создание объекта stud1
student stud2 (stud1); / / Объект stud2 - копия объекта stud1
stud3 = stud1; / / Объект stud3 равен объекту stud1
Здесь с помощью конструктора по трем параметрам инициализирован
объект stud1 значениями ("Серченко", 300, 5000.00). Затем определены еще два
объекта класса с именами stud2 и stud3, которые инициализируются такими
же значениями, как у объекта stud1. Для копирования значений полей объекта
stud1 в соответствующие поля объектов stud2 и stud3, дважды вызывается
конструктор копирования. В результате все три объекта будут иметь одинако-
вые значения, идентичные значениям объекта stud1.
Примечание. Если класс содержит указатели или ссылки, вызов конструк-
тора копирования приведет к тому, что и копия, и оригинал указывают на одну
и ту же область памяти. В таком случае конструктор копирования должен быть
создан программистом в таком виде:
<имя класса> :: < имя класса > (const < имя класса > &) {...}

В классе может быть определен не один конструктор, а несколько конст-


рукторов (с одним и тем же именем класса). Тогда говорят, что конструктор яв-
ляется перегруженным. Какой из них будет выполняться при создании объек-
тов, зависит от того, сколько аргументов используется в вызове. Если в классе
определен хоть один конструктор, то компилятор не создаст конструктора по
умолчанию. И если в классе нет конструктора по умолчанию, то в некоторых
ситуациях могут возникать ошибки. В таком случае следует определить свой
собственный конструктор по умолчанию, например:
student (): name (""),group(""), birthday(0) {}
Примечание. Конструкторы по умолчанию не присваивают начальных
значений полям класса.

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


создается конструктор без аргументов (конструктор по умолчанию) и конст-
руктор копирования. Создаваемый автоматически конструктор без аргументов
создается в таком виде:
<имя класса> :: < имя класса> (){ }
Конструктор копирования автоматически создается в таком виде:
<имя класса> :: < имя класса> (const < имя класса> &r)
{*this = r;}
Аргументом конструктора копирования всегда является объект своего
класса. Любой конструктор с параметрами является конструктором инициали-
зации. Создадим класс time и примеры конструкторов для него:
21
class time
{private:
int hours,minutes,seconds; // поля класса: часы, минуты, секунды
public:
time(); //конструктор без аргументов
time(int h, int m=0, int s=0) //конструктор инициализации
{ hours = h; minutes = m; seconds = s; }
};
time::time() //реализация вне класса
{ hours = minutes = seconds = 0;}

Конструкторы инициализации обеспечивают объявление и инициализацию


переменных:
time t(12,55,45), // hours=12, minutes=55, seconds=45;
s(13,45), // hours=13, minutes=45, seconds=0;
u(14); // hours=14, minutes=0, seconds=0;
Конструктор инициализации может вызываться явно:
time R = time(10);
Конструкторы вызываются и при создании динамических переменных.
time *a = new time; // конструктор без аргументов
time *b = new time(); // конструктор без аргументов
time *d = new time(12,23); // конструктор инициализации
time *f = new time(*d); // конструктор копирования
Конструкторы обеспечивают привычную форму объявления констант:
const time d1(100,67); //конструктор инициализации
const time d2=100; //конструктор инициализации
const time d3=time(100); //явный вызов
Конструктор копирования вызывается неявным образом при передаче в
функцию параметра-объекта по значению и при возврате из функции. При дру-
гих способах передачи параметров конструктор не вызывается.

Пример 1.2 Создать класс Interval, в котором предусмотреть конструк-


торы по умолчанию и с параметрами, а также методы вычисления суммы двух
значений времени (t1+ t2), каждое из которых представлено в виде двух целых
чисел: hour (часы) и minute (минуты). Создать объекты класса t1 и t2 и выпол-
нить для них вычисления.

Текст программы:
//--------------------------------------------
class Interval
{private:
int hour,minute;
22
public:
Interval():hour(0), minute(0){} / / Конструктор по умолчанию
Interval(int h, int m); / / Конструктор с параметром
void setinterv(int h,int m){ minute=m%60; hour=h+m/60;}
int gethour(){return hour;}
int getminute(){return minute;}
Interval add_interv(Interval);
};/ / Реализация методов
Interval::Interval(int h,int m): hour(h), minute(m)
{hour+=minute/60;minute%=60;}
Interval Interval:: add_interv(Interval d2)
{Interval temp;
temp.minute = minute+d2.minute;
temp.hour = hour+d2.hour;
if(temp.minute>59){temp.minute-=60; temp.hour++;}
return temp;
}
// Обработчик на кнопку «Сумма t1+t2»
void __fastcall TForm1::Button1Click(TObject *Sender)
{ / / Создание объектов t1 и t2 с данными из Edit
Interval t1(StrToInt(Edit1->Text),StrToInt(Edit2->Text));
Interval t2(StrToInt(Edit3->Text),StrToInt(Edit4->Text));
Interval t; / / Объект класса по умолчанию
t=t1.add_interv(t2); / / Вычисление
Edit5->Text=IntToStr(t.gethour()); / / Вывод «часов»
Edit6->Text=IntToStr(t.getminute());/ / Вывод «минут»
}
В классе Interval имеются следующие методы: конструктор по умол-
чанию и конструктор с двумя параметрами; метод setinterv(), позволяющий
на любом этапе программы изменять поля класса; методы gethour()и
getminute(), возвращающие значения полей hour и minute соответственно;
метод add_interv(), позволяющий складывать два промежутка времени.
В обработчике события OnClick кнопки с названием “Сумма t1+t2” с по-
мощью конструктора с двумя параметрами определяются два объекта t1 и t2,
инициализирующие свои поля значениями, прочитанными из четырех тексто-
вых полей, которые заполнит пользователь перед нажатием кнопки. Затем с
23
помощью конструктора по умолчанию определяется объект t, которому в сле-
дующем операторе присваивается значение, возвращаемое методом
add_interv(), который вызван объектом t1 с аргументом t2. В результате
значение объекта t представляет собой сумму двух интервалов времени t1 и
t2. С помощью методов gethour() и getminute()получим значения полей
объекта t и выведем их в предназначенные для них текстовые поля.
Форма проекта с результатами вычислений:

Пример 1.3 Создать класс rabotnic (работник), в котором предусмот-


реть методы начисления фактической зарплаты и налога, удержанного из нее. В
этом случае налог определяется следующим образом: если по основному месту
работы зарплата работника не превышает 300 гривен, то она не начисляется,
иначе − налог составляет 15% от зарплаты. Но, если работник работает по со-
вместительству, то независимо от размера зарплаты отчисляется налог в 20%.
Использовать этот клас для начисления фактической зарплаты, которую полу-
чит отдельный работник, и величины уплачиваемого налога.
Решение. Коды с объявлением класса и реализацией его методов запи-
шем соответственно в файлах Рrac.h и Рrac.cpp. Для создания этих файлов
выполним команды меню Borland C++: File / New / Unit. При этом к проекту
будут добавлены файлы Unit2.cpp и Unit2.h. Для файла Unit2.cpp выполним
команду File / Save as ... и присвоим ему новое имя Рrac.cpp (в папке проекта);
автоматически изменится имя файла Unit2.h на Рrac.h.
Запишем в файле Рrac.h объявление класса rabotnic (перед директивой
# endif).
Файл Рrac.h:
# Ifndef PracH
# Define PracH
//------------------------------------------------ -----------
/ / Объявление класса
class rabotnic
{private:
double oklad; int vid;
24
public:
/ / Конструктор по умолчанию
rabotnic (): oklad (0), vid (0) {}
/ / Конструктор с параметрами
rabotnic (double okl, int v) {oklad = okl; vid = v;}
/ / Метод вычисления зарплаты
double zarplata ();
/ / Метод вычисления налога
double nalog ();
};
# еndif
Объявленный в программе класс rabotnic содержит два поля: oklad −
для хранения значения оклада работника, и vid, которое приобретает значение
0, если эта работа является основной, и значение 1, если это работа по совмес-
тительству. Класс содержит следующие методы: конструктор по умолчанию,
конструктор с двумя параметрами; nalog () − функция, которая вычисляет
налог работника по установленному правилу; zarplata () - функция, которая
вычисляет "чистую" зарплату работника.
Примечание. Если среди методов класса есть такие, которые имеют тип
AnsiString, например:
AnsiString info()
{Return "Зарплата =" + FloatToStr (zarplata());}
тогда первой в файле необходимо записать директиву
# include <vcl.h>

Запишем в файле Рrac.cpp реализацию методов класса nalog () и zar-


plata() (после всех стандартных директив файла):
Файл Рrac.cpp
# pragma hdrstop
# include "Рrac.h" / / Автоматическое добавлениtя файла определения класса
/ / Реализация метода вычисления налога
double rabotnic :: nalog ()
{double nal;
switch (vid)
{case 0: if (oklad &lt;= 300) nal = 0;
else nal = oklad * 0.15; break;
case 1: nal = oklad * 0.2; }
return nal; }
/ / Реализация метода вычисления зарплаты
double rabotnic :: zarplata ()
{return oklad - nalog ();
}
25
Запишем в главном файле проекта − Unit1.cpp код программы для кноп-
ки "Расчет", и привлечем к нему файл определения класса Prac.h.
Главный файл проекта Unit1.cpp
# include <vcl.h>
/ / Включение файла объявления и определения методов класса
#include "Prac.h"
#pragma hdrstop
#include "Unit1.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//-------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{ }
//--------- Отклик на кнопку «Расчет» --------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{ int k;
if(RadioGrooup1->ItemIndex == 0) k=0; else k=1;
rabotnic a(StrToFloat(Edit1->Text), k); // Создание объекта a
Edit2->Text = FloatToStr(a.nalog ());
Edit3->Text = FloatToStr(a.zarplata ());
}
В программном коде для кнопки " Расчет " объявлена переменная объек-
та класса a, значения полей которого задаются с помощью конструктора с дву-
мя параметрами. Для этого объекта вызываются методы nalog()и
zarplata(), а их результаты выводятсяся в компоненты Edit. Кроме компо-
нентов Edit для ввода значения оклада работника и вывода результатов, на фор-
ме также расположен компонент RadioGroup − для избрания вида деятельности
(значение поля vid). Форма проекта имеет вид:

Возможные варианты результатов при выполнении проекта:


26

Пример 1.4 Создать класс для изображения на форме проекта геометри-


ческой фигуры − прямоугольник по заданным координатам углов: верхнего ле-
вого (x1,y1) и правого нижнего (x2,y2). Определить методы класса: конструк-
тор и функции: вычисления длины диагонали прямоугольника, изображения
заданного текста, графического изображения контура прямоугольника по за-
данным координатам и прямоугольника, закрашенного заданным цветом. Соз-
дать проект с объектами класса.
Форма проекта имеет вид:

Текст программы:

//Описание класса
class pram
{protected:
int x1,y1,x2,y2; //координаты прямоугольника − закрытые параметры
public:
pram(int ix1,int iy1,int ix2,int iy2); //конструктор
void figura();//функция изображения прямоугольника
float diag();//функция вычисления длины диагонали
27
void figurum(TColor); //функция избражения прямоугольника,
// закрашенного заданным цветом
void textcol(AnsiString s); //функция изображения заданного текста
};

// Реализация конструктора
pram:: pram(int ix1,int iy1,int ix2,int iy2)
{x1=ix1;y1=iy1;
x2=ix2;y2=iy2;
}
//Реализация функции изображения прямоугольника по размерам полей класса
void pram::figura()
{ Form1->Image1->Canvas->Brush->Color = clBlue;
Form1->Image1->Canvas->Rectangle(x1,y1,x2,y2);
}

// Реализация функции изображения прямоугольника, закрашенного заданным цветом


void pram::figurum(TColor c)
{ Form1->Image1->Canvas->Brush->Color = c;
Form1->Image1->Canvas->Rectangle(x1,y1,x2,y2);
}

// Реализация функции изображения заданного текста


void pram::textcol(AnsiString s)
{ Form1->Image1->Canvas->Pen->Color = clGreen;
Form1->Image1->Canvas->Brush->Color = clWhite;
Form1->Image1->Canvas->TextOut(x1+30,y1-20,s);
}

// Реализация функции расчета диагонали


float pram::diag()
{return sqrt(pow(x2-x1,2)+ pow(y2-y1,2));}

// ----------------Кнопка «ПОКАЗАТЬ»-----------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Memo1->Clear();// очистка Memo1 для результатов длины прямоугольника

// объект а1 – с параметрами-константами (вертикальный прямоугольник слева)


pram a1(10,10,100,200);
a1.figura();
Image1->Canvas->TextOut(20,220, "Объект 1");
Memo1->Lines->Add("d1="+FloatToStr(a1.diag()));

// объект а2 – с параметрами из Edit


int x1,x2,y1,y2;
x1=StrToInt(Edit1->Text);
y1=StrToInt(Edit2->Text);
x2=StrToInt(Edit3->Text);
y2=StrToInt(Edit4->Text);
28
pram a2(x1,y1,x2,y2);
// объект вывод надписи "Объект 2"
Image1->Canvas->TextOut(x1+30,y2+20, "Объект 2");
// изображение прямоугольника, закрашенного красным цветом
a2.figurum(clRed);
// вывод заданного текста
AnsiString s="Прямоугольник "; a2.textcol(s);
// вывод значения диагонали прямоугольника
Memo1->Lines->Add("d2="+FloatToStr(a2.diag()));
}
Пример 1.5 Создать класс для динамической структуры, называемой оче-
редь. Структура содержит два информационных поля: фамилия студента и эк-
заменационный балл по информатике. Класс будет иметь следующие методы:
конструктор, добавление элемента в очередь, удаление элемента из очереди,
просмотр элементов очереди.
Внешний вид рабочей формы имеет вид:

Текст программы
// описание динамической структуры «очередь»
struct listec
{String Fio; // фамилия студента
int num; // балл по дисциплине информатика
listec *next; // указатель на следующий элемент
};
// описание класса
class queue
{private:
listec *curr, / / поля класса - указатели на текущий,
*first, / / первый
*last; / / и последний элементы класса
public:
queue(){curr=first=last=NULL; } // конструктор класса
void add(String fio,int d); // функция добавления элемента в очередь
void del(); // функция удаления элемента из очереди
AnsiString view(); // функция просмотра содержимого очереди
};
29
// реализация функциии добавления элемента в очередь
void queue::add(String fio,int d)
{ curr=new listec;
curr->num=d;
curr->Fio=fio;
curr->next=0;
if (first==0) first=curr; else last->next=curr;
last=curr;
}
// реализация функциии удаления элемента из очереди
void queue::del()
{ curr=first;
if (first==0){ShowMessage("Нет ничего!");return;}
first=first->next;
delete curr;
}
// реализация функциии просмотра содержимого очереди
AnsiString queue::view()
{AnsiString s="";
if(curr==0){ShowMessage("Нет ничего!");s="";return s;}
while (curr!=0)
{s=s+curr->Fio+" "+IntToStr(curr->num)+"\r\n";
curr=curr->next; }
return s;
}

// глобальная переменная объекта класса queue


queue stt;
// кнопка “Добавить” − добавление в очередь очередного студента
void __fastcall TForm1::Button1Click(TObject *Sender)
{ String fio=Edit1->Text;;
int d=StrToInt(Edit2->Text);
stt.add(fio,d);
Edit1->Text="";
Edit2->Text="";
Edit1->SetFocus();
Button2Click(0);
}
// кнопка “Просмотр” − просмотр содержимого созданного списка
void __fastcall TForm1::Button2Click(TObject *Sender)
{ Memo1->Clear();
Memo1->Lines->Add(stt.view());
}
// кнопка “Удалить” − удалить элемент списка из очереди
void __fastcall TForm1::Button3Click(TObject *Sender)
{ stt.del();
Button2Click(0);
}
30
1.6 Деструкторы

Функцией, обратной конструктору, является деструктор. Это, как и конст-


руктор, специальная функция, которая вызывается для уничтожения (удаления
из памяти) объекта класса. Имя деструктора также совпадает с именем класса,
но перед ним записывается символ тильда (~). Деструкторы необходимы, если
конструктор, или какие-то другие функции класса динамически распределяют
память, создавая какие-то объекты. Тогда деструктор должен удалять эти объ-
екты. В остальных случаях можно обойтись без деструктора.
Если деструктор явным образом не задан, то компилятор сам генерирует
необходимые коды освобождения памяти.
Деструктор выглядит так:
~<имя класса>(){}
Рассмотрим основные свойства деструктора.
− Имя деструктора начинается с тильды (~), непосредственно за которой
следует имя класса.
− Деструктор − это метод, который выполняется автоматически.
− Конструктор не возвращает никакого значения, даже типа void.
− Деструктор не имеет аргументов.
− Указатель на деструктор определить нельзя.
− Деструктор не наследуется.
− Если деструктор явно не определен, компилятор автоматически создает
пустой деструктор.
− По завершении программы объекты всех классов уничтожаются из па-
мяти компьютера даже если деструктор явно не определено.

В предыдущих программах объекты создавались после их объявления.


По завершении программы автоматически вызывался деструктор для каждого
объекта, хотя он не был явно указан в программе.
Деструктор, кроме деинициализации объектов, может выполнять некото-
рые действия, например, вывод окончательных значений элементов данных
класса, что бывает удобно при отладке программы. В качестве примера, опре-
делим класс student с деструктором и конструктором:
class student / / Объявление имени класса
{ private: // закрытые поля класса
AnsiString name;
AnsiString group;
int birthday;
public: // общедоступные методы класса
student (AnsiString iname, AnsiString igroup, int ibirthday); // Конструктор
void ~ student (void); // Деструктор
AnsiString show_student (void); //Функция информации о студенте
int year();//Функция вычисления количества лет студенту в 2011 году
};
31

Реализация деструктора для класса student (вне класса):


void student::~ student (void)
{ Form1-> Memo1->Lines->Add
("Объект удален из памяти "+ show_student()); }
Этот деструктор выведет в Memo информацию о том, какой объект уда-
лен из памяти.
Явно размещать деструктор в классе нужно в случае, если объект содер-
жит указатели на память, выделенную как динамическая, − иначе, при уничто-
жении объекта, память, на которую ссылались его поля-указатели, не будет оп-
ределена как освобожденная. Ниже приведен пример использования деструкто-
ра для статической переменной MyInt и динамической переменной *point.
сlass CMyClass
{ public:
CMyClass(); // Конструктор класса CMyClass
~CMyClass(); // Деструктор класса CMyClass
private:
int MyInt; // Переменная типа int (целое число)
int *point; // Переменная типа указатель на int (целое число)
};

CMyClass::CMyClass()// Конструктор
{ MyInt = 10; // На этапе инициализации объекта класса CMyClass
// переменной MyInt этого объекта присваивается значение 10
point = new int; // Выделение памяти по целое число для указателя
*point = 20; // Запись в выделенную память числа 20
}

CMyClass::~CMyClass()// Деструктор
{ MyInt = 0; // Объект класса CMyClass фактически прекратил своё
// существование,но мы присвоим переменной MyInt значение 0

delete point; // Используем указатель на число для того,


// чтобы освободить память, которая выделена под это число
// (Автоматически деструктор не выполнит это!)
}

Пример 1.6 Создадим класс "два резистора, которые соединены парал-


лельно" с полями номиналов r1 и r2. В программе используем различные спо-
собы определения конструкторов: без параметров (конструктор 1), с одним
(конструктор 2) и с двумя параметрами (конструктор 3). Кроме того, явно оп-
ределим деструктор в объявлении класса и вычислим общее сопротивление
этих резисторов. Программу разместим в двух файлах Unit1.h и Unit1.cpp, на-
значение операторов приведено в комментариях.
32
Файл Unit1.h − объявление и описание класса и его функций
/ / Объявление класса
class resistor / / Класс - два параллельных резистора
{private:
float r1, r2; / / Поля класса - значение номиналов резисторов
public:
resistor (float iR1): r1 (iR1), r2 (1e9) {} / / Конструктор 1
resistor (): r1 (1e9), r2 (20) {} / / Конструктор 2
resistor (float iR1,float iR2): r1(iR1), r2(iR2 ){};// Конструктор 3
float comr (); / / Метод класса - вычисление совместного сопротивления
/ / двух резисторов
AnsiString resistor:: Info ();// Метод класса – формирование строки
/ / информации о резисторах
~ Resistor (void);/ / Деструктор
};
/ / Описание реализации функций (методов) класса
float resistor:: comr ()
{Return r1 * r2 / (r1 + r2);}

AnsiString resistor:: Info ()


{Return "r1 =" + FloatToStr (r1) + "r2 =" + FloatToStr (r2);}

resistor:: ~ resistor (void) / / Реализация деструктора


{Form1-> Memo1->Lines->Add ("Объект уничтожен ("+ Info ()+")");}
Файл Unit1. cpp − создание и удаление объектов класса

//--------- Отклик на кнопку «Объекты класса» --------------


void __fastcall TForm1:: Button1Click (TObject * Sender)
{resistor a11; / / Создание объекта класса a11 конструктор 1
float ra1 = a11.comr();/ / Вычисление совместного сопротивления для объекта a11
Memo1->Clear();
/ / Вывод результатов для объекта a11
Memo1->Lines->Add (a11.Info() + "r_a11 =" + FloatToStr(ra1));
a11.~ resistor(); / / Уничтожение объекта a11 деструктором
resistor a12(40); / / Создание объекта класса a12 конструктором 2
float A2 = A12.comr(); / / Вычисление совместного сопротивления для объекта a12
/ / Вывод результатов для объекта a12
Memo1->Lines->Add (a12.Info() + "r_a12 =" + FloatToStr(ra2));
resistor a13(20,20); / / Создание объекта класса a13 конструктором3
float ra3 = a13.comr();/ / Вычисление совместного сопротивления для объекта a13
/ / Вывод результатов для объекта a13
Memo1->Lines->Add (a13.Info() + "r_a13 =" + FloatToStr(ra3));
}
33
Результаты работы программы − значение общего сопротивления двух па-
раллельных резисторов для трех объектов, созданных с помощью конструкто-
ров разного вида и с явным использованием деструктора, − приведены на изо-
бражении формы проекта (рис. 1.1).

Рисунок 1.1 − Результаты выполнения программы примера 1.6


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

Пример 1.7 Создадим программу с классом − матрицей размерности nxn и


методами вычисления значений:
- матрицы по формуле
4 ⋅ (i + j + 1)
aij = 2.5i + j +1 +
3
, i = 0,..,4, j = 0,...,4;
2 + ( j + 1) 2
- элементов вектора, как суммы элементов строк матрицы;
- скаляра, как сумму элементов вектора.

Файл Unit2.h
class matr / / Объявление класса
{ float *b; / / указатель на матрицу
float *c; / / / /указатель на вектор
int n; / /указатель на размер матрицы
public:
matr( float *ib, int in,float *ic); / /конструктор
void vmatr(float *b, int n, floa *c); / /вычисление элементов матрицы
void vect(float *b,int n, float *c); / / вычисление элементов вектора
34
float func(float *b, int n, float *c); / / вычисление скаляра
};
Файл Unit2.cpp
#pragma hdrstop
#include <Math.h>
#include "Unit2.h"
#pragma package(smart_init)
/ / реализация конструктор
matr::matr(float *ib, int in,float *ic)
{ b=ib; n=in; c=ic;}
/ /вычисление элементов матрицы
void matr::vmatr(float *u, int n, float *c)
{for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
*(u+i*n+j)=2.5*pow((i+j+1),1./3)+4*(i+j+1)/(2+pow((j+1),2));
}
/ / вычисление элементов вектора
void matr::vect(float *b, int n, float *c )
{
float s;
for(int i=0;i<n;i++)
{s=0;
for(int j=0;j<n;j++)
s=s+*(b+i*n+j);
*(c+i)=s;
}
}
/ / вычисление скаляра
float matr::func(float *b, int n, float *d)
{
float s=0;
for(int i=0;i<n;i++)
s=s+c[i];
return s;
}
Главный модуль − Unit1.cpp
#include <vcl.h>
#pragma hdrstop
#include <Math.h>
#include "Unit1.h"
#include "Unit2.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
35
: TForm(Owner)
{
}
//------кнопка «Вычисление» ----------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int k=4;
float *a=new float [k*k]; / / объявление динамической матрицы a
float *C=new float [k]; / / объявление динамического вектора C
matr A(a,k,C); / / создание объекта класса − матрица A
A.vmatr(a,k,C); / / Вычичсление значений матрицы
/ / Вывод значений матрицы
for(int i=0;i<k;i++)
for(int j=0;j<k;j++)
SG1->Cells[j+1][i+1]=FloatToStrF(*(a+i*k+j),ffFixed,5,3);
/ / Вывод значений вектора C
A.vect (a,k,C);
for(int i=0;i<k;i++)
SG2->Cells[i][1]=FloatToStrF(*(C+i),ffFixed,5,3);
/ / Вычисление и вывод значений скаляра z
float z=A.func(a,k,C);
Edit2->Text=FloatToStr(z);
delete []a;
delete []C;
}
//------------ кнопка «Очистка» компонентов формы----------------
void __fastcall TForm1::Button7Click(TObject *Sender)
{
AnsiString s="";
s=s+" "+""+" "+""+" "+""+" ";
SG2->Rows[1]->DelimitedText=s;
for(int i=1;i<=4;i++)
SG1->Rows[i]->DelimitedText=s;
Edit2->Text="";
}
//---------кнопка «Выход»----------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{ Close();}
Форма проекта с результатами имеет вид:
36

}
37

Л а б о р а т о р н а я р а б о т а № 2.1

Программирование создания классов и их объектов

Цель работы. Изучить элементы объектно-ориентированного программи-


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

Контрольные вопросы

1. В чем сходны и чем отличаются в C++ понятия структуры и класса?


2. Что такое инкапсуляция?
3. Что называется классом?
4. Можно ли описать метод класса вне класса?
5. Как соотносятся между собой понятия объекта и класса?
6. Для чего нужен раздел private в объявлении класса? Приведите при-
меры данных или методов, которые целесообразно помещать в этот раздел.
7. Для чего нужен раздел public в объявлении класса?
8. Что такое конструктор и когда он вызывается?
9. Может ли класс иметь несколько конструкторов?
10. Что такое конструктор по умолчанию и когда он вызывается?
11. Что такое конструктор копирования и когда он вызывается?
12. Что такое деструктор и когда он вызывается?
13. Может ли в классе быть несколько деструкторов?

Лабораторное задание

1 Изучить теретический материал и дать ответы на контрольные вопросы.


2 Составить программы на языке С++ в соответствии с индивидуальным
заданием для базового, среднего и высокого уровней.
3 Оформить протокол лабораторной работы.
4 Выполнить программы на компьютере и записать результаты их выпол-
нения.

1 Базовый уровень
Для всех вариантов заданий создать класс с указанными в таблице 1.1 (ко-
лонка 2) полями и тремя методами:
− конструктор для инициализации объекта;
− функция формирования строки с информацией об объекте;
− функция обработки значений полей по индивидуальному варианту.
38
В основной программе вводить значения полей каждого объекта из компо-
нентов Edit и выводить результаты в компонент Memo. Индивидуальные вари-
анты заданий приведены в таблице 1.1.

Таблица 1.1 − Варианты индивидуальных заданий


вар. Класс и его поля Функция обработки полей
1 Денежные купюры: номинал (1, 2, 5, 10 Вычислить сумму купюр
и т.д.) и количество
2 Монеты: номинал(1, 2, 5, 10 и т.д.) и Вычислить сумму монет
количество
3 Товар: цена и количество Вычислить общую стои-
мость товара
4 Продукты питания: калорийность 100 Вычислить общую кало-
г и вес продукта в граммах рийность продукта
5 Диапазон вещественных чисел: левая и Квадрат длины диапазона
правая границы диапазона
6 Время: минуты и секунды Вычислить общее количе-
ство секунд
7 Время: часы и минуты Вычислить общее количе-
ство минут
8 Катеты прямоугольного прямоуголь- Вычислить площадь прямо-
ника: вещественные числа к1 и к2 угольного треугольника
9 Параметры движения: скорость дви- Вычислить пройденное рас-
жения (м/cек) и время движения в ми- стояние (в метрах)
нутах
10 Катеты прямоугольного прямоуголь- Вычислить длину гипотену-
ника: вещественные числа а1 и а2 зы прямоугольного тре-
угольника
11 Параметры трапеции: верхнее и ниж- Вычислить полусумму ос-
нее основание трапеции нований трапеции
12 Катеты прямоугольного прямоуголь- Вычислить тангенс угла α ,
ника: вещественные числа р1 и р2 противолежащего второму
катету прямоугольного тре-
угольника
13 Пара вещественных чисел: z1 и z2 Вычислить полуразность
чисел
14 Пара вещественных чисел: р1 и р2 Вычислить корень квадрат-
ный из произведения чисел
15 Пара целых чисел: x и y Вычислить целую часть от
деления х на y
16 Пара целых чисел: a и b Вычислить квадрат мень-
шего из чисел
39

вар. Класс и его поля Функция обработки полей
17 Пара целых чисел: n и m Вычислить куб большего из
чисел
18 Телефонный разговор: продолжитель- Вычислить общую стои-
ность телефонного разговора в мину- мость разговора
тах и стоимость одной минуты разго-
вора
19 Точка на плоскости: координаты точки Определить периметр пря-
на плоскости (по горизонтали и верти- моугольника, ограниченно-
кали) го координатами точки и
осями Ox и Oy
20 Два вещественных числа: a и b Вычислить разность квад-
ратов чисел a2 – b2
21 Два вещественных числа: a и b Вычислить сумму квадра-
тов чисел a2 + b2
22 Точка на плоскости: координаты точки Определить площадь пря-
на плоскости (по горизонтали и верти- моугольника, ограниченно-
кали) x1 и y1 го координатами точки и
осями Ox и Oy
23 Точка на плоскости: координаты точки Вычислить расстояние от
на плоскости (по горизонтали и верти- точки до начала координат
кали) x1 и y1
24 Параметры работы: количество часов Общая стоимость работы
работы и тариф оплаты за час работы
25 Дуга: радиус окружности и угол в ра- Вычислить длину дуги
дианах
26 Цилиндр: радиус окружности основа- Вычислить площадь по-
ния и высота цилиндра верхности цилиндра

27 Конус: радиус окружности основания Вычислить объем конуса


конуса и высота конуса
28 Резистор: напряжение (в Вольтах) и Вычислить значение тока (в
сопротивление (в Омах) Амперах)
29 Участок цепи: ток в амперах и сопро- Вычислить мощность на
тивление резистора R1(в Омах) участке электрической цепи
(в Ваттах)
30 Движущееся тело: масса тела – m (в Вычислить кинетическую
граммах) и скорость движения – v (в энергию движущегося тела
м/с) mv 2
Wk =
2
40
2 Средний уровень
Создать класс с полями, указанными в индивидуальном задании (табл. 1.2,
столб. 2).
Реализовать в классе методы:
- конструктор по умолчанию;
- конструктор перезагрузки с параметрами;
- деструктор для освобождения памяти (с сообщением об уничтожении
объекта);
- функции-методы обработки данных (1 и 2), указанные в индивидуальном
задании (табл. 1.2, столбцы 3 и 4);
- функцию формирования строки информации об объекте.
Создать проект для демонстрации работы: сформировать объекты со зна-
чениями-константами и с введенными значениями полей объекта из компонен-
тов Edit. Выводить результаты в компонент Memo.

Таблица 1.2 – Варианты индивидуальных заданий


№ Класс-родитель и Функция-метод 1 Функция-метод 2
вар. его поля обработки данных обработки данных
1 Дата (три числа): Определить, является Увеличить дату на 5
день, месяц, год ли год высокосным дней
(кратным 4)
2 Дата (три числа): Увеличить год на 1 Уменьшить дату на 2 дня
день, месяц, год
3 Дата (три числа): Определить, совпадают Увеличить дату на один
день, месяц, год ли номер месяца и чис- месяц
ло дня
4 Время (три числа): Вычислить количество Увеличить время на 5 се-
часы, минуты, се- секунд в указанном кунд
кунды времени
5 Время (три числа): Вычислить количество Уменьшить время на 10
часы, минуты, се- полных минут в ука- минут
кунды занном времени
6 Время (три числа): Определить количество Увеличить время на 100
часы, минуты, се- минут до полуночи минут
кунды (24:00:00)
7 Координаты изо- Вычислить площадь Изобразить прямоуголь-
бражения прямо- прямоугольника в пик- ник на форме
угольника: x1, y1, селях (Image) с толщиной ли-
x2, y2 нии 2 пикселя
8 Координаты изо- Вычислить квадрат Изобразить прямоуголь-
бражения прямо- длины диагонали пря- ник и его диагональ на
угольника: x1, y1, моугольника в пиксе- форме
x2, y2 лях (Image)
41

№ Класс-родитель и Функция-метод 1 Функция-метод 2


вар. его поля обработки данных обработки данных
9 Координаты изо- Определить, является ли Изобразить прямоуголь-
бражения прямо- прямоугольник квадра- ник на форме
угольника: x1, y1, том? (Image), закрашенный
x2, y2 зеленым цветом
10 Правильная дробь: Выразить значение дро- Найти сумму цифр зна-
числитель, знаме- би в процентах чения знаменателя
натель
11 Комплексное чис- Вычислить модуль ком- Найти комплексное чис-
ло: действительная плексного числа ло, обратное заданному
(a1) и мнимая (b1)
части числа
12 Комплексное чис- Вычислить произведе- Вычислить аргумент
ло: действительная ние комплексного числа комплексного числа в
и мнимая часть на число, вводимое градусах
числа пользователем
13 Книга: название, Вычислить среднюю Увеличить цену книги в
количество стра- стоимость одной стра- два раза, если название
ниц, цена ницы начинается со слова
«Программирование»
14 Книга: название, Вычислить, сколько лет Количество дней, про-
автор, год издания книге шедших после года из-
дания книги
15 Работник: фами- Вычислить стаж работы Сколько дней прошло
лия, оклад, год по- работника на данном после года поступления
ступления на рабо- предприятии. на работу
ту
16 Работник: фами- Вычислить возраст ра- Сколько календарных
лия, оклад, год ро- ботника дней до исполнения ра-
ждения ботнику 50 лет
17 Вектор на плоско- Вычислить длину векто- Изобразить линию век-
сти: координаты ра тора на форме (Image) с
вектора на плоско- толщиной линии 2 пик-
сти (x1, y1, x2, y2) селя
18 Вектор на плоско- Вычислить координаты Равен ли угол наклона
сти: координаты середины вектора вектора 45 градусов?
вектора на плоско-
сти (x1, y1, x2, y2)
19 Вектор на плоско- Вычислить координаты Вычислить площадь пря-
сти: координаты вектора, удвоенной дли- моугольного треуголь-
вектора на плоско- ны ника, образованного за-
сти (x1, y1, x2, y2) данным вектором и пря-
мыми, параллельными
осям Оx, Оy
42

№ Класс-родитель Функция-метод 1 Функция-метод 2


вар. и его поля обработки данных обработки данных
20 Цилиндр: диа- Вычислить объем Изобразить круг заданного
метр основания, цилиндра диаметра на форме (Image),
высота закрашенный красным цветом
21 Параллелепипед: Вычислить объем Вычислить длину наибольшей
длины сторон параллелепипеда диагонали параллелепипеда
22 Параллелепипед: Вычислить площадь Вычислить сумму длин всех
длины сторон поверхности ребер параллелепипеда
23 Четыре целых Вычислить среднее Определить максимальное из
числа: a, b ,c, d арифметическое чи- чисел
сел
24 Три веществен- Вычислить среднее Определите, сколько цифр
ных числа x, y, z геометрическое чи- содержит сумма заданных
сел трех чисел
25 Товар: наимено- Определить, сколько Увеличить цену товара на
вание, цена, год лет назад был выпу- 20%, если в наименовании то-
выпуска щен товар вара есть слово “TV”
26 Товар: наимено- Пересчитать цену Увеличить цену товара в дол-
вание, цена в товара в долларах ларах, если название товара
гривне, изгото- содержит слово “Toyota”
витель
27 Координаты изо- Определить, являет- Изобразить эллипс на форме
бражения эллип- ся ли эллипс окруж- (Image) зеленым цветом
са: x1, y1, x2, y2 ностью?
28 Книга: название, Увеличить количест- Уменьшить цену в два раза,
количество стра- во страниц на 10. если количество страниц
ниц, цена больше 100 (после увеличе-
ния)
29 Комната: длина, Площадь стен (вме- Площадь стен без окна (раз-
ширина, высота сте с окнами и мер 2х1.5 м) и двери (размер
(в метрах) дверьми) 2 х 0.8 м)
30 Работник: фами- Увеличить оклад на Работникам, у которых фами-
лия, должность, 15% (каждому ра- лия начинается с сочетания
оклад ботнику) букв “Иван“, присвоить
должность “инженер“

3 Высокий уровень

Создать класс с полями, указанными в индивидуальном задании (табл. 1.3).


Реализовать в классе методы:
- конструктор по умолчанию;
- конструктор перезагрузки с параметрами;
43
- деструктор для освобождения памяти (с объекты со значениями-
константами и с введенными значениями полей объекта из компонентов Edit.
Выводить результаты в компонент Memo.
Таблица 1.3 – Варианты индивидуальных заданий
№ Задание
вар.
1 Создать класс вектор, задаваемый тройкой координат (x, y, z). Реализо-
вать функции: сложения и вычитания векторов, скалярное произведение
векторов, вычисление длины вектора.
2 Создать класс деньги для работы с денежными суммами. Число должно
быть представлено двумя полями: long для гривни и unsigned char для
копеек. Дробная часть (копейки) при выводе должна быть отделена от
целой части (гривни) запятой. Реализовать сложение, вычитание, деле-
ние сумм, деление суммы на дробное число, умножение на дробное чис-
ло (для пересчета в другие денежные единицы по заданному курсу).
3 Создать класс треугольник для вычисления его числовых характеристик.
Поля данных должны включать стороны. Реализовать функции: вычис-
ления периметра, площади, всех углов и высот треугольника; определе-
ния вида треугольника (равносторонний, равнобедренный, прямоуголь-
ный).
4 Создать класс два угла для работы с углами на плоскости, которые зада-
ются в градусах с минутами. Реализовать функции: перевод значений
углов из минут с секундами в радианы, приведение к диапазону 0-360,
сложение и вычитание углов, получение синуса и тангенса угла.
5 Создать класс комплексные числа, которые представляются парой веще-
ственных чисел (a,b) и (c,d), где a, c – действительная часть чисел,
b,d − мнимая часть чисел. Реализовать функции: сложения, вычитания,
умножения, деления чисел; формирования сопряженного комплексного
числа и проверки двух комплексных чисел на равенство.
6 Создать класс рациональные дроби, которые представлены парой чисел
(a,b) и (c,d), где a, c − числители, b,d − знаменатель. Реализовать функ-
ции: сложения, вычитания, умножения, деления и сокращения дробей;
сравнения значений дробей (меньше, равны или больше).
7 Создать класс дата, которое представляется тремя полями типа unsigned
int для года, месяца и дня. Дата должна инициализироваться двумя кон-
структорами в форматах строки «год.месяц.день» и трех целых чисел.
Реализовать функции: вычисление даты через заданное количество дней
или месяцев, вычитание заданного количества дней из даты, выделение
из даты отдельных её частей (года, месяца, дня).
8 Создать класс время, которое представляется тремя полями типа un-
signed int для часов, минут и секунд. Время должно инициализироваться
двумя конструкторами в форматах строки «часы.минуты.секунды» и
трех целых чисел. Реализовать функции: вычисление времени через за-
данное количество минут или секунд, вычитание заданного количества
44
минут из времени, выделение из времени отдельных его частей (часы,
минуты, секунды).
9 Релизовать класс нечеткие числа, которые представляются тройками чи-
сел: A=(A−al, A, A+ar) B=(B−bl, b, B+br). Реализовать функции:
- сложения A+B =(A+B−al −bl, A+B, A+ B +ar+br);
- вычитания A−B =(A−B−al −bl, A−B, A− B +ar+br);
- умножения A х B=
=(A х B−B х al −A х bl+ al х bl, A х B, A х B+B х ar +A х br+ ar х br);
- обратное нечеткое числоA-1= (1/(A+ar), 1/A,1/(A−al)), A>0;
- A/B=((A−al)/(B+br),A/B,(A+ar)/(B−bl), B>0.
10 Создать класс товар с полями: наименование товара, дата получения,
цена товара, количество единиц товара, номер накладной, по которой то-
вар поступил на склад. Реализовать функции: изменения цены товара,
изменения количества товара, вычисления стоимости товара, определе-
ния количества дней, месяцев и лет от получения товара.

11 Создать класс BitString для работы с 64-битовыми строками. Битовая


строка должна быть представлена двумя полями типа unsigned long. Реа-
лизовать функции для операций с битами: and, or, xor, not. Реализовать
сдвиг влево и сдвиг вправо на заданное количество битов.

12 Создать класс LongLong для работы с целыми числами из 64-бит. Число


должно быть представлено двумя полями: long − старшая часть,
unsigned long − младшая часть. Реализовать функции для операций с
числами: сложение, вычитание, умножение, деление, деление нацело, ос-
таток от деления.
13 Создать класс зарплата с полями: фамилия, имя, отчество, оклад, дата
поступления на работу, процент подоходного налога, количество рабо-
чих дней в месяце. Реализовать функции: вычисления подоходного нало-
га, стажа работы на предприятии, надбавок за стаж, зарплаты (суммы,
выдаваемой на руки работнику). Надбавки за стаж составляют 10% окла-
да при стаже от 5 до 10 лет, 20% − при стаже более 10 лет. Из оклада
удерживается подоходный налог и отчисления в пенсионный фонд в раз-
мере 1% оклада.
14 Создать класс банковский счет с полями: фамилия и инициалы, номер
счета, процент начисления, сумма на счету в гривне. Реализовать функ-
ции: смены владельца счета, снять сумму со счета, добавить сумму на
счет, начислить проценты, перевести сумму в доллары, перевести сумму
в евро, записать сумму прописью (словами).
45

2 НАСЛЕДОВАНИЕ КЛАССОВ

Наиболее значимой возможностью объектно-ориентированного програм-


мирования является наследование классов. Наследование (inheritance) − это
процесс создания новых классов, из уже существующих базовых классов. В
этом случае, класс, который наследуется, называется базовым классом или
классом-родителем; класс, который является наследником, называется произ-
водным классом или потомком класса.
Наследование является очень важным свойством в программировании,
так как он позволяет поддерживать принцип иерархии классов (hierarchical clas-
sification). Например, класс мобильных телефонов является подклассом класса
“Телефон”, который в свою очередь входит в ещё больший класс “Электро-
связь”. Вместе с тем, класс “Электросвязь” является подклассом класса “Спо-
собы связи”, в состав которого, кроме электросвязи, входит и спутниковая
связь, и радиосвязь, и почтовая связь и т. п. (рис. 2.1). Использование иерархии
классов позволяет управлять большими потоками информации. Примерами по-
добной иерархии являются также системи класификации в ботанике, зоологии
и других областях науки и техники.

Способы связи Базовый класс

Классы 1-го уровня

Электро- Спутниковая Почтовая


Радиосвязь ...
связь связь связь

Телеграф Телефон ... Классы 2-го уровня

Мобильный Стационарный ... Классы 3-го уровня

Рисунок 2.1 − Иерархия классов “Способы связи”


Наследование бывает простым и множественным. При простом наследо-
вании производный класс имеет только одного родителя. Множественное на-
следование означает, что класс имеет несколько базовых классов, и применяет-
ся для того, чтобы обеспечить производный класс свойствами двух или более
базовых. Производный класс, в свою очередь, сам может служить базовым
классом.
Обычно процесс наследования начинается с задания базового класса. Про-
изводный класс получает все возможности родительского, или базового класса,
но может тоже быть усовершенствован за счет изменения существующих ме-
тодов и добавления собственных полей и методов. Базовый класс при этом ос-
46
тается неизменным. Это очень удобно, т. к. программист может использовать
классы, созданные кем-то другим, без модификации кода, просто создавая про-
изводные классы, подходящие для конкретной ситуации.
Для создания производного класса используется ключевое слово class,
после которого указывается имя нового класса, двоеточие, ключ доступа клас-
са, а затем имя базового класса по формату:
class
<имя_производного_класса>:[<ключ_доступа>]<имя_базового_класса>
{ <тело_класса> };
В приведенном определении производного класса должен использоваться
один из следующих ключей доступа: public (oткрытый), private (закрытый)
или protected (защищенный).
Элементы базового класса, объявленные как private, в производном клас-
се недоступны вне зависимости от ключа доступа. Обращение к ним может
осуществляться только через методы базового класса. Для базовых классов
возможно использование еще одного спецификатора − protected, который для
одиночных классов, не входящих в иерархию, равносилен private.
Особенности использования ключей доступа при наследовании:
− ключ доступа public означает, что все открытые и защищенные члены
базового класса становятся таковыми и для производного класса;
− ключ доступа private означает, что все открытые и защищенные члены
базового класса являются закрытыми членами для производного класса;
− ключ доступа protected означает, что все открытые и защищенные чле-
ны базового класса становятся защищенными членами производного класса.
Рассмотрим наследование классов на примерах.

Пример 2.1 Создадим класс Triangle, описывающий правильный тре-


угольник. Его поле а – сторону треугольника - объявим с ключом доступа
protected (защищенный от всех, кроме производного класса). Определим
внутри класса следующие методы:
− конструктор по умолчанию и конструктор с одним параметром;
− методы seta() и geta(), изменяющий и возвращающий сторону тре-
угольника;
− методы hh(), P() и S()возвращают высоту, периметр и площадь соот-
ветственно правильного треугольника.
Создадим класс Piramid − производный от класса Triangle, описывающий
правильный трехгранник. В этом классе добавим private-поле h – высоту пи-
рамиды и public-методы V(), Hbok() и Sbok(), возвращающие объем, высоту
боковой грани и площадь поверхности соответственно правильной пирамиды.
Конструкторы не наследуются, а значит, производный класс должен иметь
собственные конструкторы. Поэтому определим в классе два собственных кон-
47
структора: по умолчанию и с двумя параметрами, инициализирующими знче-
ния стороны основания пирамиды и ее высоты.
В этом примере иерархия классов − простая: один базовый класс и один
производный класс (рис. 2.2).

Класс – Triangle (треугольник)

Класс – Piramid (пирамида)

Рисунок 2.2 − Иерархия классов для примера 2.1


Текст программы:
// Описание базового класса
class Triangle
{protected:
double a;
public:
Triangle():a(0){} // конструктор по умолчанию (a = 0)
Triangle(double at):a(at){} // конструктор с двумя параметрами
void seta(double at){a=at;} // метод присвоения значения стороны a
double geta(){return a;} // метод получения значения стороны a
double ht(){return a*sqrt(3)/2;} // метод вычисления высоты боковой грани
double P(){return 3*a;} // метод вычисления периметра треугольника
double S(){return a*ht()/2;} // метод вычисления площади основания
// пирамиды
};

// Описание производного класса


class Piramid: public Triangle
{ private:
double h; // высота
public:
Piramid():Triangle(),h(0){} // конструктор по умолчанию
Piramid(double ap, double hp):Triangle(ap),h(hp){} // конструктор
// с двумя параметрами
double V(){return S()*h/3;} // метод вычисления объема пирамиды
double Hbok(){return sqrt(pow(ht()/3,2)+pow(h,2));} // метод
//вычисления высоты боковой грани
double Sbok(){return P()*Hbok()/2;} // метод вычисления площади
// боковой поверхности пирамиды
};
А теперь создадим приложение, демонстрирующее работу с базовым клас-
сом Triangle и классом-наследником Piramid. Определим объект d класса
48
Piramid с помощью конструктора с двумя параметрами. Зачения стороны тре-
угольника и высоты пирамиды будем вводить с клавиатуры через компоненты
Edit. Выведем на форму площадь основания, объем и площадь поверхности
заданной пользователем правильной треугольной пирамиды.
Код функции, выполняющейся по щелчку на кнопке «Рассчитать», имеет
вид:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Piramid d(StrToFloat(Edit1->Text),StrToFloat(Edit2->Text));
Edit3->Text=FloatToStr(d.S()); // Вывод площади основания пирамиды
Edit4->Text=FloatToStr(d.V()); // Вывод объема пирамиды
Edit5->Text=FloatToStr(d.Sbok()); // Вывод площади боковой поверхности
}
Форма приложения имеет вид:

Пример 2.2 Создать класс – “два числа”, у которого имеется функция


для вычисления суммы этих чисел. Используя поля и методы этого класса соз-
дать два производных класса: “прямоугольник”, который вычисляет площадь
прямоугольника, и “цилиндр”, который вычисляет площадь поверхности ци-
линдра. Для класса “прямоугольник” создать производный класс “параллелепи-
пед”, который вычисляет поверхности параллелепипеда с использованием па-
раметра – значения высоты. Иерария этих классов показана на рис. 2.3.

Класс – два числа

Класс – прямоугольник Класс – цилиндр

Класс – параллелепипед

Рисунок 2.3 −Иерархия классов для примера 2.2


49
Текст программы

class para // класс para – базовый класс


{protected: // Обеспечение доступа производного класса к полям базового класса
float heigth, width ;
public:
para():heigth(10), width(20){}; // конструктор со списком значений
para (float c1,float c2); // конструктор с присвоением
~para (void); // деструктор
float sum ();
};

class rectangle : public para // класс rectangle − потомок класса para


{public:
rectangle(float h, float w);
float area();
};

class cylinder : public para // класс cylinder потомок класса para


{ public:
cylinder (float h, float w);
float area();
};

class paral:public rectangle // класс paral потомок потомка класса para


{private:
float hv;
public:
paral(float h, float w,float hh);
float area();
};

para:: para(float c1,float c2) // конструктор родителя


{ heigth=c1; width=c2;}

para::~ para(void) // деструктор


{ Form1->Memo1->Lines->Add(" удалён объект h="+
FloatToStr(heigth)+" w="+FloatToStr(width));}

float para::sum()// метод вычисления суммы двух чисел


{return heigth+width;}

rectangle::rectangle(float h, float w):para(h,w){}; // конструктор потомка

float rectangle::area() // метод вычисления площади прямоугольника


{return rectangle::heigth*rectangle::width;}
50
cylinder::cylinder(float h, float w):para(h,w){}; // конструктор потомка
//{ heigth=h; width=w;}

float cylinder::area() //метод вычисления площади поверхности цилиндра


{return 2*3.14*width*width/4+3.14*width*heigth;}

paral::paral(float h, float w,float hh): rectangle(h, w) //конструктор


// потомка
{hv=hh;}

float paral::area()//метод вычисления площади поверхности параллелипипеда


{return 2*heigth*width+2*para::sum()*hv;
}
//------ кнопка « Показать» результаты ---------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
para p(StrToFloat(Edit1->Text),StrToFloat(Edit2->Text));
Memo1->Lines->Add("сумма чисел="+FloatToStr(p.sum()));
p.~para(); // явный вызов деструктора

para p2;
Memo1->Lines->Add("Периметр прямоугольника="+FloatToStr(2*p2.sum()));

rectangle b(StrToFloat(Edit1->Text),StrToFloat(Edit2->Text));
Memo1->Lines->Add("Площадь прямоугольника="+FloatToStr(b.area()));

cylinder c(StrToFloat(Edit3->Text),StrToFloat(Edit4->Text));
Memo1->Lines->Add("Поверх. цилиндра="+FloatToStr(c.area()));

paral d(StrToFloat(Edit1->Text),StrToFloat(Edit2->Text),10);
Memo1->Lines->Add("Поверхн. параллелипипеда="+FloatToStr(d.area()));
}
Форма проекта с результатами вычислений имеет вид:
51
Пример 2.3 Создадим класс «матрица», полями которой является указа-
тель на квадратную матрицу размером nхn. Метод этого класса транспонирует
исходную матрицу. На базе этого класса создадим производный класс (пото-
мок), функции которого вычисляют евклидову норму (корень квадратный из
суммы квадратов элементов матрицы) и заменяют элементы первого столбца
матрицы на суммы элементов строк.

Текст программы
// Описание базового класса
class matr
{protected:
int *b; / / объявление динамической матрицы b
int n; / / размер матрицы
public:
matr( int *ib, int k);
void transp(int *u,int k);
};

// ---------реализация конструктора---------------------------------------------------
matr:: matr( int *ib, int k)
{b=ib; n=k;}

// ----------Функция транспонирования матрицы----------


void matr::transp(int *u,int k)
{ int i,j;
for(i=0;i<k;i++)
for(j=i+1;j<k;j++)
{ int p=*(u+k*i+j);
*(u+n*i+j)=*(u+k*j+i);
*(u+k*j+i)=p; }
}
// Класс- потомок
class matrchild: public matr
{
public:
float norm(); //евклидова норма
matrchild(int *ib, int k); //Конструктор
void sumzam(); // замена 1-го столбца матрицы
};

// Конструктор потомка
matrchild::matrchild(int *ib, int k):matr(ib,k){}
// Функция потомка − вычисление евклидовой нормы матрицы
float matrchild::norm()
{
int i,j,s=0;
52
for(i=0;i<n;i++)
for(j=0;j<n;j++)
s=s+pow(*(b+n*i+j),2);
return sqrt(s);}

// Функция потомка − замена 1-го столбца матрицы суммой строк


void matrchild::sumzam()
{int i,j,sm[10];
for(i=0;i<n;i++)
{sm[i]=0;
for(j=0;j<n;j++)
sm[i]=sm[i]+(*(b+n*i+j));
}
for(i=0;i<n;i++)
*(b+n*i+1)=sm[i];
}

// -------------Кнопка «Транспонировать»-----------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{ int m=StrToInt(Edit1->Text);
int *x; int i,j;
x=(int*)malloc(m*m*sizeof(int));
for(i=0;i<m;i++)
for(j=0;j<m;j++)
*(x+i*m+j) =StrToInt(StringGrid1->Cells[j+1][i+1]); //
matr ob1(x,m); // объект − родитель
ob1.transp(x,m); // вычисление по методу объекта-родителя
for(i=0;i<m;i++)
for(j=0;j<m;j++)
StringGrid2->Cells[j+1][i+1]=IntToStr(*(x+i*m+j));
}

// -------------Кнопка «Заменить 1-й столбец суммой строк»------------------------


void __fastcall TForm1::Button2Click(TObject *Sender)
{ int m=StrToInt(Edit1->Text);
int i,j;

matrchild ob2(x,m); // объект класса-потомка

float nor=ob2.norm();// вычисление нормы матрицы по методу объекта-потомка


Edit2->Text=FloatToStr(nor);
// вычисление элементов матрицы по методу объекта-потомка
ob2.sumzam();
// вывод элементов матрицы после замены элементов
for(i=0;i<m;i++)
for(j=0;j<m;j++)
StringGrid3->Cells[j+1][i+1]=IntToStr(*(x+i*m+j));
}
53
Форма проекта с результатами вычислений имеет вид:

Пример 2.4 Создать классы, которые позволяют обрабатывать динамиче-


ские структуры типа стек и очередь. В качестве базового класса будем исполь-
зовать общий принцип построения динамических структур. Иерархия классов
приведена на рис. 2.3.

Класс – структура list (список)

Класс – структура steck (стек) Класс – структура queue (очередь)


Рисунок 2.3 − Структура классов для примера 2.4
Текст программы (в файле Unit1.cpp):

class list // начало описания базового класса


{private:
struct listec // начало описания структуры
{String Fio;
int num;
listec *next;
}; // конец описания структуры
public: listec *curr,*first,*last;
list(){curr=first=last=NULL;} //конструктор
void add(String, int );
void del();
AnsiString view();
}; // конец описания базового класса

class steck : public list // начало описания производного класса steck


{ public:
void add(String fio, int d);
54
void del();
AnsiString view();
}; // конец описания производного класса

void steck::add(String fio,int d)


{ curr=new listec;
curr->num=d;
curr->Fio=fio;
curr->next=last;
last=curr;
}
void steck::del()
{ curr=last;
if (curr==0){ShowMessage("Нет ничего!");return;}
last=last->next;
delete curr;
}
AnsiString steck::view()
{curr=last;
AnsiString s="";
if (curr==0) {ShowMessage("Нет ничего!");s="";return s;}
while (curr!=0)
{s=s+curr->Fio+" "+IntToStr(curr->num)+"\r\n";
curr=curr->next;
}
return s;
}

class queue : public list // начало описания производного класса queue


{
public:
void add(String fio, int d);
void del();
AnsiString view();
};

void queue::add(String fio,int d)


{
curr=new listec;
curr->num=d;
curr->Fio=fio;
curr->next=0;
if (first==0) first=curr; else last->next=curr;
last=curr;
}
void queue::del()
{ curr=first;
if (curr==0){ShowMessage("Нет ничего!");return;}
first=first->next;
55
delete curr;
}
AnsiString queue::view()
{curr=first;
AnsiString s="";
if (curr==0) {ShowMessage("Нет ничего!");s="";return s;}
while (curr!=0)
{s=s+curr->Fio+" "+IntToStr(curr->num)+"\r\n";
curr=curr->next;
}
return s;
}

//объявление объектов классов steck и queue


steck st; queue stt;

// Подпрограммы откликов на кнопки проекта:

//------ кнопка « Добавить в стек» ---------


void __fastcall TForm1::Button1Click(TObject *Sender)
{
String fio=Edit1->Text;;
int d=StrToInt(Edit2->Text);
st.add(fio,d);
Edit1->Text="";
Edit2->Text="";
Edit1->SetFocus();
Button2Click(0);
}
//------ кнопка « Удалить из стека» ---------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
st.del();
Button2Click(0);
}
//------ кнопка « Просмотр» для стека ---------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Memo1->Clear();
Memo1->Lines->Add(st.view());
}
//------ кнопка « Добавить в очередь» ---------
void __fastcall TForm1::Button5Click(TObject *Sender)
{
String fio=Edit1->Text;;
int d=StrToInt(Edit2->Text);
stt.add(fio,d);
Edit1->Text="";
Edit2->Text="";
56
Edit1->SetFocus();
Button6Click(0);
}

//------ кнопка «Просмотреть» ---------


void __fastcall TForm1::Button6Click(TObject *Sender)
{
Memo2->Clear();
Memo2->Lines->Add(stt.view());
}

//------ кнопка «Удалить из очереди» ---------


void __fastcall TForm1::Button7Click(TObject *Sender)
{
stt.del();
Button6Click(0);
}

//------ кнопка «Выход» ---------


void __fastcall TForm1::Button4Click(TObject *Sender)
{
Close();
}

Как видно из приведенного, в качестве базового класса создан класс, в ко-


тором вставлено описание списка (в разделе private), и описаны общедоступ-
ные поля, которые инициируются в конструкторе по умолчанию. Каждый про-
изводный класс (стек и очередь), наследуют эти общедоступные переменные и
методы обработки списков (прототипы функций). В самих производных клас-
сах конкретизируются функции обработки каждого из производных классов.
Внешний вид рабочей формы может иметь следующий вид:
57
Пример 2.5 Создадим программу с базовым классом − матрицей размер-
ности nxn, у которой производным является класс − вектор, у которого в свою
очередь есть производный класс − скаляр (рис. 2.4). Элементы матрицы вычис-
лить по формуле
1,5 − i ( j + 1) 2 + 0,8
aij = + , i = 0,..,4, j = 0,...,4;
i + j + 1 (i + 1)(2.5 − j )
элементы вектора вычислить как максимальные значения элементов строк мат-
рицы; скаляр вычислить как длину вектора (корень квадратный из суммы эле-
ментов вектора в квадрате).

Класс – матрица

Класс – вектор

Класс – скаляр

Рисунок 2.4 −Иерархия классов для примера 2.5


Текст программы:
#include <vcl.h>
#pragma hdrstop
#include <Math.h>
#include "Unit1.h"
//---------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
typedef float mat [5][5]; // Описание типа для матрицы
typedef float vec [5]; // Описание типа для вектора

class matr // Описание класса для матрицы


{ mat b; int n; int mk;
public:
matr( int in, int im) ; // Конструктор класса matr
// Метод вычисления значений элементов матрицы
void set_b(mat b );
};
58
class vect : public matr // Описание класса для вектора
{ vec d;
public:
vect( int in, int im); // Конструктор класса vect
// Метод вычисления значений элементов вектора
void set_v(mat b, vec d, int n, int mk);
};

class func : public vect // Описание класса для скаляра


{
public:
func(int in, int im); // Конструктор класса func
// Метод вычисления значений скаляра
float gfunc(vec d, int n, int mk);
};

matr::matr(int in, int im) // Реализация конструктор класса matr


{n=in; mk=im;}

// Реализация метода вычисления элементов матрицы


void matr::set_b(mat b)
{for (int i=0;i<n;i++)
for (int j=0;j<mk;j++)
b[i][j]=(1.5-i)/(i+j+1)+(pow((j+1),2)+0.5)/((i+1)*(2.5-j));}

// Реализация конструктор класса vect


vect::vect(int in,int im) :matr(in,im) {}

// Реализация метода вычисления элементов вектора


void vect::set_v(mat b, vec d ,int n, int mk)
{
float max;
for(int i=0;i<n;i++)
{max=b[i][0];
for(int j=0;j<mk;j++)
if (b[i][j]>max)
max=b[i][j];
d[i]=max; }}

// Реализация конструктор класса func


func::func(int in, int im):vect(in,im) {}

// Реализация метода вычисления значения скаляра


float func::gfunc(vec d, int n, int mk)
{ float s=0;
for(int i=0;i<n;i++)
s=s+pow(d[i],2);
59
return sqrt(s);}

//---------Функция создания формы------------------------


void __fastcall TForm1::FormCreate(TObject *Sender)
{
for (int i=0;i<m;i++)
{SG1->Cells[i+1][0]=IntToStr(i);
SG2->Cells[i][0]=IntToStr(i);}
for(int j=0;j<k;j++)
SG1->Cells[0][j+1]=IntToStr(j);
}

// Описаниеглобальных констант и массивов


int k=5; int m=5; mat a; vec x;

//--------Кнопка «Определяем элементы матрицы»---------------


void __fastcall TForm1::Button1Click(TObject *Sender)
{
matr cm(k,m); // Создание объекта − матрицы
cm.set_b(a); // Вычисление элементов матрицы
// Вывод значений элементов матрицы на форму
for(int i=0;i<k;i++)
for(int j=0;j<m;j++)
SG1->Cells[j+1][i+1]=FloatToStrF(a[i][j],ffFixed,5,2);
for(int j=0;j<k;j++)
SG1->Cells[0][j+1]=IntToStr(j);
}

//--------Кнопка «Определяем элементы вектора»---------------


void __fastcall TForm1::Button2Click(TObject *Sender)
{
vect cv(k,m); // Создание объекта − вектора
cv.set_v(a,x,k,m); // Вычисление элементов вектора
// Вывод значений элементов вектора на форму
for(int j=0;j<m;j++)
SG2->Cells[j][1]=FloatToStrF(x[j],ffFixed,5,2);
}

//--------Кнопка «Определяем функцию»---------------


void __fastcall TForm1::Button3Click(TObject *Sender)
{ func fg(k,m); // Создание объекта − скаляра
float g=fg.gfunc(x, k, m); // Вычисление скаляра
Edit2->Text=FloatToStr(g); // Вывод значения скаляра на форму
}

//--------Кнопка «Выход»---------------
void __fastcall TForm1::Button4Click(TObject *Sender)
60
{
Close();
}
//--------Кнопка «Очистка »---------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{
AnsiString s="";
s=s+" "+""+" "+""+" "+""+" ";
SG2->Rows[1]->DelimitedText=s;
for(int i=1;i<=k;i++)
SG1->Rows[i]->DelimitedText=s;
Edit2->Text="";
}

Форма проекта с результатами вычислений имеет вид:


61

Л а б о р а т о р н а я р а б о т а № 2.2

Программирование классов с иерархической структурой

Цель работы. Изучить понятия и принципы наследования классов; нау-


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

Контрольные вопросы

1. Что такое наследование? Приведите примеры.


2. Что такое иерархия классов?
3. Что такое простое наследование?
4. Что такое множественное наследование?
5. Как объявляется производный класс?
6. Что означает ключ доступа protected?
7. Будет ли правильным утверждение: создание производного класса по-
требует коренных изменений в базовом классе?
8. Будет ли правильным утверждение: если конструктор производного
класса не определен, то объекты этого класса будут использовать конструкторы
базового класса?

Лабораторное задание

1 Изучить теоретический материал и дать ответы на контрольные вопросы.


2 Составить программы на языке С++ в соответствии с индивидуальным
заданием для базового, среднего и высокого уровней.
3 Оформить протокол лабораторной работы.
4 Выполнить программы на компьютере и записать результаты их выпол-
нения.
1 Базовый уровень
Для класса, созданного в предыдущем задании (по вариантам табл. 1.1)
создать класс-потомок с дополнительным полем, указанными в индивидуаль-
ном задании (табл. 2.1, столб.3).
Реализовать в классе-потомке методы:
− конструктор;
− функцию обработки данных, указанную в индивидуальном задании
(табл. 2.1, столб. 4).
Создать проект для демонстрации работы: ввод и вывод информации об
объектах: классе-родителе и классе-потомке.
62
Таблица 2.1 – Варианты индивидуальных заданий
№ Класс-родитель Поле класса-
вар. и его поля потомка Функция обработки
(из табл. 1.1) (дополнительное) данных класса-потомка
1 Денежные купюры: но- Стоимость одного Стоимость купюр в евро
минал (1, 2, 5, 10 и т.д.) и евро (€)в гривне
количество
2 Монеты: номинал(1, 2, 5, Стоимость одного Стоимость монет в цен-
10 и т.д.) и количество $ (доллара) в тах
гривне
3 Товар: цена и количество Год выпуска това- Сколько лет товару
ра

4 Продукты питания: кало- Количество вита- Количество витамина С в


рийность 100 г и вес про- мина С в 1 грамме продукте
дукта в граммах продукта
5 Диапазон вещественных Вещественное чис- Проверить, принадлежит
чисел: левая и правая гра- ло х ли число х заданному
ницы диапазона диапазону
6 Время: минуты и секунды Скорость движе- Расстояние, пройденное
ния объекта на- объектом наблюдения
блюдения (в м/сек)
7 Время: часы и минуты Длительность вы- Сколько операций мож-
полнения одной но выполнить за указан-
операции в мину- ное время
тах
8 Катеты прямоугольного Высота призмы Объем призмы, у кото-
прямоугольника: вещест- рой в основании прямо-
венные числа к1 и к2 угольный треугольник
9 Параметры движения: Сила, приложен- Количество работы, вы-
скорость движения ная к движущему- полненной при прямоли-
(м/cек) и время движения ся объекту нейном перемещении
в минутах объекта
10 Катеты прямоугольного Высота призмы Сумма всех ребер приз-
прямоугольника: вещест- мы, у которой в основа-
венные числа а1 и а2 нии прямоугольный тре-
угольник
11 Параметры трапеции: Высота трапеции Площадь трапеции
верхнее и нижнее основа-
ние трапеции
12 Катеты прямоугольного Угол β (в радиа- Разность между задан-
прямоугольника: вещест- нах) ным углом β и углом α
венные числа р1 и р2 (результат функции
классса-родителя)
63
№ Класс-родитель Поле класса-
вар. и его поля потомка Функция обработки
(из табл. 1.1) (дополнительное) данных класса-потомка

13 Пара вещественных чи- Вещественное Определить произведе-


сел: z1 и z2 число – с ние полуразности чисел
класса-родителя (a и b)
на число с
14 Пара вещественных чи- Вещественное Вычислить выражение
сел: р1 и р2 число – z xy + z , где x и y – поля
класса-родителя
15 Пара целых чисел: x и y Вещественное Вычислить выражение
число – z x y
+ , где x и y – поля
z z
класса-родителя
16 Пара целых чисел: a и b Вещественное Произведение числа z на
число – z минимальное из чисел x
и y (поля класса-
родителя)
17 Пара целых чисел: n и m Вещественное Сумма куба числа z и
число – z максимального из чисел
x и y (поля класса-
родителя)
18 Телефонный разговор: Количество разго- Общая стоимость разго-
продолжительность теле- воров по телефону воров за сутки
фонного разговора в ми- за сутки
нутах и стоимость одной
минуты разговора
19 Точка на плоскости: ко- Вещественное Увеличить обе коорди-
ординаты точки на плос- число – с наты точки на с и найти
кости (по горизонтали и их произведение
вертикали)
20 Два вещественных числа: Вещественное Вычислить для заданно-
aиb число – х го числа х значение вы-
ражения a x2 + b
21 Два вещественных числа: Вещественное Вычислить для заданно-
aиb число – с го числа c корень урав-
нения a x + b = c

22 Точка на плоскости: ко- Координаты вто- Найти расстояние между


ординаты точки на плос- рой точки на плос- первой и второй точкой
кости (по горизонтали и кости: x2, y2
вертикали) x1 и y1
64
№ Класс-родитель Поле класса-
вар. и его поля потомка Функция обработки
(из табл. 1.1) (дополнительное) данных класса-потомка
23 Точка на плоскости: ко- Радиус окружно- Определить, находится
ординаты точки на плос- сти ли точка с параметрами
кости (по горизонтали и x1, y1 (класса-родителя)
вертикали) x1 и y1 внутри окружности с
центром в начале коор-
динат
24 Параметры работы: коли- Число – подоход- Вычислить, сколько де-
чество часов работы и та- ный налог в про- нег получит работник,
риф оплаты за час работы центах если вычтут подоходный
налог
25 Дуга: радиус окружности Число – высота Вычислить объем фигу-
и угол в радианах объемного сектора ры, у которой в основа-
нии сектор окружности с
параметрами класса-
родителя
26 Цилиндр: радиус окруж- Количество оди- Общая площадь поверх-
ности основания и высота наковых цилинд- ностей цилиндров
цилиндра ров
27 Конус: радиус окружно- Высота отпилен- Объем усеченной пира-
сти основания конуса и ной сверху части миды, оставшейся после
высота конуса конуса отпиливания
28 Резистор: напряжение (в Время в секундах Работа, выполненная ре-
Вольтах) и сопротивление зистором за указанное
(в Омах) время
29 Участок цепи: ток в ампе- Сопротивление Определить общую мощ-
рах и сопротивление ре- второго, последо- ность на двух резисторах
зистора R1(в Омах) вательно соеди-
ненного резистора
R2
30 Движущееся тело: масса Высота располо- Определить потенциаль-
тела – m (в граммах) и жения движущего- ную энергию тела
скорость движения – v (в ся тела
м/с)

2 Средний уровень

Для класса, созданного в предыдущем задании (по вариантам табл. 1.2)


создать класс-потомок с полями и функциями, указанными в индивидуальном
задании (табл. 2.2, столб.3).
Реализовать в классе-потомке методы:
- конструктор;
65
- деструктор для освобождения памяти (с сообщением об уничтожении
объекта);
- функцию обработки данных, указанную в индивидуальном задании
(табл. 2.2, столб. 4);
- функцию формирования строки информации об объекте.
Создать проект для демонстрации работы: ввод и вывод информации об
объектах: классе-родителе и классе-потомке.

Таблица 2.2 – Варианты индивидуальных заданий


Класс-родитель Класс-потомок и его Функция-метод обра-
№ и его поля поля ботки данных объекта
вар. (из табл. 1.2) (поля класса-родителя класса-потомка
выделены курсивом)
1 Дата (три числа): Список друзей: ФИО, Количество дней до дня
день, месяц, год телефон, дата рожде- очередного рождения
ния,
2 Дата (три числа): Работник: ФИО, дата Количество лет работы на
день, месяц, год поступления на пред- предприятии
приятие
3 Дата (три числа): Лекарство: наименова- Сколько прошло дней от
день, месяц, год ние, дата выпуска, изготовления лекарства
фирма
4 Время (три чис- Расписание занятий: Какая дисциплина по рас-
ла): часы, мину- дисциплина, время нача- писанию начинается в
ты, секунды ла, аудитория указанное время
5 Время (три чис- Расписание движения Количество минут до от-
ла): часы, мину- поездов: номер поезда, правления поезда с ука-
ты, секунды направление, время от- занным номером и вве-
правления денное время
6 Время (три чис- Абонент мобильной свя- Определить, является ли
ла): часы, мину- зи: фамилия, оператор, время льготным для або-
ты, секунды текущее время нента (время от 0 до 8 ча-
сов)
7 Координаты изо- Изображение конверта Площадь верхнего (над-
бражения прямо- (прямоугольник с ли- диагонального) треуголь-
угольника: x1, y1, ниями диагоналей): ника в пикселях
x2, y2 координаты прямоуголь-
ника, цвет линий
66

8 Координаты изо- Изображение прямо- Площадь фигуры между


бражения прямо- угольника с вписанным прямоугольником и кру-
угольника: x1, y1, в его центр кругом: ко- гом
x2, y2 ординаты прямоуголь-
ника, радиус круга R
(R< x2 – x1, R< y2 – y1)
9 Координаты изо- Изображение закрашен- Произведение периметра
бражения прямо- ного прямоугольника с и длины диагонали пря-
угольника: x1, y1, текстом: координаты моугольника в пикселях
x2, y2 прямоугольника, задан-
ный текст, цвет закра-
шивания
10 Правильная дробь: Смешанная дробь: целая Представить смешанную
числитель, знаме- часть, числитель и зна- дробь в виде десятичного
натель менатель вещественного числа
11 Комплексное чис- Два комплексных числа: Вычислить произведение
ло: действительная действительная (a1) и двух комплексных чисел
(a1) и мнимая (b1) мнимая (b1) части пер-
части числа вого числа; действитель-
ная (a2) и мнимая (b2)
части второго числа
12 Комплексное чис- Комплексное сопротив- Вычислить модуль и ар-
ло: действительная ление: сопротивление гумент комплексной про-
и мнимая часть резистора (действитель- водимости участка цепи
числа ная часть), значение ин- «резистор – индуктив-
дуктивности (мнимая ность»
часть), угловая частота
13 Книга: название, Библиотека: название, Стоимость книги с учетом
количество стра- количество страниц, це- скидки
ниц, цена на, скидка в процентах
14 Книга: название, Книжный магазин: на- Уменьшить стоимость
автор, год издания звание, автор, год изда- книги на 20%, если книге
ния, цена больше 5 лет
15 Работник: фамилия, Работники предприятия: Определить сколько лет
оклад, год поступ- фамилия, оклад, год по- нужно работать работни-
ления на работу ступления на работу, ку до 60 лет, а если ему
год рождения больше 60, то сколько лет
он работает после 60 лет
16 Работник: фами- Работники фирмы: фа- Увеличить оклад работ-
лия, оклад, год ро- милия, оклад, год рож- никам с должностью про-
ждения дения, должность граммист на 20%
67

17 Вектор на плоско- Вектор и точка на плос- Определить площадь тре-


сти: координаты кости: координаты век- угольника, образованного
вектора на плоско- тора (x1, y1, x2, y2); ко- вектором и точкой
сти (x1, y1, x2, y2) ординаты точки – x3, y3
18 Вектор на плоско- Два вектора с общим на- Определить координаты
сти: координаты чалом ( x1, y1) на плос- вектора суммы двух век-
вектора на плоско- кости: координаты пер- торов
сти (x1, y1, x2, y2) вого вектора – x1, y1, x2,
y2; координаты второго
вектора – x1, y1, x3, y3
19 Вектор на плоско- Два параллельных век- Определить площадь па-
сти: координаты тора на плоскости оди- раллелограмма, образо-
вектора на плоско- наковой длины: коорди- ванного этими векторами
сти (x1, y1, x2, y2) наты первого вектора – и линиями, соединяющи-
x1, y1, x2, y2; второй ми их начала и концы
вектор смещен вправо по
оси Ох на величину а,
второй – вниз по оси Оу
на величину b
20 Цилиндр: диаметр Изолированный провод: Определить вес изолиро-
основания, высота диаметр, длина, удель- ванного провода
ный вес; толщина изоля-
ции и её удельный вес
21 Параллелепипед: Металлический брус: Определить вес металли-
длины сторон ширина, высота, длина, ческого бруса
удельный вес
22 Параллелепипед: Балка с прямоугольным Площадь поверхности од-
длины сторон сечением: ширина, вы- ной части распиленной
сота, длина, удельный балки и её вес
вес; количество равных
частей, на которые её
распилят
23 Четыре целых чис- Пять чисел: четыре це- Вычислить сумму квадра-
ла: a, b ,c, d лых числа (a,d,c,d) и чис- тов разности каждого из
ло х четырех чисел и числа х
24 Три вещественных Два набора чисел: три Определить скалярное
числа: x,y,z вещественных числа произведение двух набо-
x,y,z и три веществен- ров чисел
ных числа a, b ,c
25 Товар: наименова- Фирменный товар: на- Количество дней после
ние, цена, год вы- именование, цена, год года выпуска товара до
пуска выпуска, дата поступле- текущего дня
ния товара
68

26 Товар: наименова- Товар: наименование, Изменить стоимость то-


ние, цена в гривне, цена в гривне, изготови- вара с учетом скидки для
изготовитель тель, год выпуска, скид- товаров, изготовленных
ка в процентах фирмой более двух лет
назад
27 Координаты изо- Дуга эллипса: коорди- Построить изображение
бражения эллипса: наты изображения эл- дуги эллипса на форме
x1, y1, x2, y2 липса x1, y1, x2, y2, ко- (Image) синим цветом,
ординаты концов дуги толщиной линии 2 пиксе-
x3, y3, x4, y4 ля
28 Книга: название, Изданная книга: назва- Сколько дней книга нахо-
количество стра- ние, количество стра- дилась в типографии
ниц, цена ниц, цена, автор книги,
дата поступления в ти-
пографию
29 Комната: длина, Помещения для офисов: Определить количество
ширина, высота (в длина, ширина, высота краски, необходимое для
метрах) комнат, количество покраски стен и потолка
комнат и площадь кори- помещений офиса (в каж-
дора, расход краски на 1 дой комнате одно окно
м2 размером 2х1.5 м)
30 Работник: фамилия, Работники предприятия: Увеличить оклад работ-
должность, оклад фамилия, должность, ников на 20%, если их
оклад, рейтинг (в 100- рейтинг от 60 до 75 балов,
бальной системе) на 40%, если их рейтинг
от 75 до 90 балов, на
60%, если их рейтинг от
90 до 100 балов

3 Высокий уровень

Создать класс и класс-потомок с полями, указанными в индивидуальном


задании (табл. 2.3); под некоторые из полей классов память выделять динами-
чески.
Реализовать в классе и классе-потомке методы:
- конструкторы по умолчанию и с параметрами;
- деструкторы;
- функции обработки данных, указанные в индивидуальном задании (табл.
2.3);
- функции формирования строки информации об объекте.
Создать проект для демонстрации работы: ввод и вывод информации об
объектах: классе-родителе и классе-потомке.
69
Таблица 2.3 – Варианты индивидуальных заданий
№ Класс-родитель Функция для Параметр Функция для
вар. класса-родителя класса- класса-потомка
потомка
1 Квадратная мат- Вектор сумм поло- Вещественное Количество эле-
рица целых чи- жительных элемен- число х ментов матрицы,
сел: элементы тов строк которые больше
матрицы и её среднего значения
размер n положительных
элементов матри-
цы
2 Квадратная мат- Вектор сумм отри- Вектор из n Скалярное произ-
рица целых чи- цательных элемен- вещественных ведение заданного
сел: элементы тов столбцов чисел х вектора и вычис-
матрицы и её ленного вектора
размер n сумм столбцов
3 Две строки раз- Объединить строки Строка S3 Объединить стро-
ной длины: через символ «про- ки и определить
S1 и S2 бел» общую сумму
цифр
4 Две строки раз- Все строчные бук- Символ Сколько раз встре-
ной длины: вы (русские и ла- чается символ-па-
S1 и S2 тинские) преобра- раметр в строках
зовать в прописные
5 Прямоугольная Вектор максималь- Целое число х Среднее значение
матрица целых ных значений строк чисел вектора, ко-
чисел: элемен- торые больше за-
ты матрицы и её данного числа
размеры nxm
6 Прямоугольная Вектор минималь- Вектор n эле- Объединить два
матрица вещест- ных значений ментов вектора и отсор-
венных чисел: столбцов тировать их в по-
элементы мат- рядке возрастания
рицы и её разме-
ры nxm
7 Прямоугольная Строка с макси- Строка m сим- Сумма кодов сим-
матрица симво- мальным кодом волов волов строки
лов: элемен-ты символов
матрицы и её
размеры nxm
8 Две строки раз- Строка общих сим- Строка сим- Сумма цифр об-
ной длины: волов двух строк волов S3 щих символов
S1 и S2 строк
70
№ Класс-родитель Функция для Параметр Функция для
класса-родителя класса- класса-потомка
потомка
9 Две строки раз- Строка из спецсим- Символ с Вставить символ с
ной длины: волов (не букв и не в строку после
S1 и S2 цифр) двух строк каждого символа #
10 Вектор из n дат Минимальная дата Текущая дата Количество дней
между минималь-
ной и текущей да-
той
11 Вектор из n слов Слово с макси- Слово S Символы слова в
(строк) мальной длиной алфавитном по-
рядке
12 Стек с информа- Добавление ин- Дата приобре- Удаление из стека
цией о дисках: формации о новом тения диска информации о са-
тип диска, объем диске; просмотр мом старом диске;
памяти информации о дис- сведения о диске с
ках максимальным
объемом
13 Очередь (на Добавление ин- Количество Сортировка по
квартиру) с ин- формации о новом проживающих возрастанию даты
формацией: фа- нуждающемся на на имеющей- постановки на
милия с инициа- жилплощадь; про- ся площади учет; удаление из
лами, дата по- смотр информации очереди тех, у ко-
становки на об очередниках на го на одного про-
учет, имеющаяся квартиру живающего более
площадь 6 кв.метров;
Удаление из оче-
реди трех первых
очередников (по-
лучивших ордер)
14 Список поступ- Добавление ин- Дата выдачи Удаление из спи-
лений книг в формации о новой книги читате- ска сведений о
библиотеку с книге; просмотр лю; фамилия книгах, которым
информацией: информации о кни- читателя более 10 лет; вы-
автор, название, гах; сортировка вод списка книг,
год издания информации по ал- которые выданы
фавиту фамилий читателям более
авторов 30 дней назад
71

3 ПОЛИМОРФИЗМ

При наследовании некоторые методы класса могут быть заменены на дру-


гие. Так, родительский класс способов связи (см. рис. 2.1) будет иметь обоб-
щенный метод − средство передачи информации. В производных классах этот
метод будет уточнен: радиосвязь − это передача радиосигналов от радиостан-
ций к радиоприемникам, почтовая связь − это перевозка транспортом почтовых
отправлений (посылок, бандеролей, писем и т.п.), спутниковая связь − это пе-
редача сигналов на спутники и прием этих сигналов спутниковыми антеннами.
Итак, одно и то же имя метода используется для решения нескольких похожих,
но технически разных задач. Такое изменение содержания метода называется
полиморфизмом. Вообще полиморфизм (polymorphism) (от греческого -
polymorphos) − это способность объекта изменять форму (poly означает много,
а morphism имеет отношение к изменениям формы). Полиморфный объект
представляет собой объект, который может принимать разнообразные формы.
Цель полиморфизма в объектно-ориентированном программировании − это
использование одного имени для подобных действий (методов) класса. Выпол-
нение каждого конкретного действия будет определено типом данных. В раз-
ных языках программирования полиморфизм реализован различными способа-
ми. Например, в Pascal и C++ он реализован с помощью механизма виртуаль-
ных функций. Вместе с тем, в языке С++ полиморфизм поддерживается недос-
таточно, например, вычисление абсолютного значения переменной можно вы-
полнить тремя функциями: abs (), labs () и fabs (). Эти функции вычисляют и
возвращают абсолютное значение переменных целого, длинного целого и дей-
ствительного типов соответственно. В Pascal такая задача выполняется одной
функцией abs (). В языке С++ выбор конкретной функции для этой задачи осу-
ществляет программист в соответствии с типом данных.
Полиморфизм может также применяться и к операторам. Фактически во
всех языках программирования ограниченно используется полиморфизм в
арифметических операциях. Так, в языке С++, символ плюс (+) используется
для добавления целых, длинных целых, вещественных чисел, а также для сим-
вольных переменных и строк. В таком случае компилятор автоматически опре-
деляет, какой тип арифметики следует применить.
Преимуществом полиморфизма является то, что он помогает уменьшить
сложность программ. Выбор конкретной функции, исходя из ситуации, возло-
жен на компилятор, а не на программиста. Необходимо только помнить и ис-
пользовать общий интерфейс. Пример с функцией вычисления абсолютного
значения переменной, показывает, как при наличии трех имен функции в языке
С++ вместо одного, любая задача становится более сложной, чем это действи-
тельно нужно.
Чтобы использовать полиморфизм, необходимо выполнять условия:
− все производные классы должны являться наследниками одного и того
же базового класса;
72
− необходимо переопределять некоторые методы базового класса в произ-
водном классе; тогда эти методы в базовом классе должны быть объявлены как
виртуальные.
Виртуальный – означает видимый, но не существующий в реальности. Ко-
гда используются виртуальные функции, программа, которая, казалось бы, вы-
зывает функцию одного класса, может в этот момент вызывать функцию совсем
другого класса. Функция объявляется виртуальной с помощью ключевого слова
virtual, предшествующего прототипу функции в базовом классе по формату:
virtual <тип> <имя_функции> (<список_параметров>);
Виртуальные функции определяются в родительском классе, в объявле-
нии этого класса записывают только прототипы функций с ключевым словом
virtual перед ними. Производный класс переопределяет эти функции, приспо-
сабливая их для собственных нужд. И, если функции были объявлены как вир-
туальные, они будут оставаться виртуальными и во всех производных классах.
Однако, как правило, предпочитают хранить этот спецификатор и в классах-
потомках для большей понятности сути этих классов.
Базовый класс часто бывает абстрактным классом, объекты которого ни-
когда не будут реализованы. Такой класс существует с единственной целью −
быть родительским относительно производных классов, объекты которых бу-
дут реализованы, чтобы создать иерархическую структуру. Например, класс
"Способы связи" имеет метод "Средства передачи информации", который не
может быть реализован для объектов этого класса, а в производных классах
этот метод имеет конкретное значение: для класса "Радиосвязь" − это радио-
сигнал, для класса "Почтовая связь" − это транспорт для перевозки почтовых
отправлений и т.п. Но, если объекты родительского (абстрактного) класса не
предназначены для реализации, то каким образом защитить базовый класс от
использования не по назначению? − Защитить его надо программно. Для этого
достаточно ввести в класс хотя бы одну чисто виртуальную функцию (pure
virtual function). Для чисто виртуальной функции используется формат:
virtual <тип> <имя_функции> (<список_параметров >) = 0;
Ключевой частью этого объявления является присвоение чисто виртуаль-
ной функции значения ноль. Это сообщает компилятору, что в родительском
классе нет тела функции. Если функция задана как чисто виртуальная, то это
означает, что она обязательно должна быть заменена в каждом производном
классе, иначе при компиляции возникнет ошибка. Следовательно, создание
чисто виртуальных функций − это гарантия того, что производные классы обес-
печат их переопределение. Если класс содержит хотя бы одну чисто виртуаль-
ную функцию, то его называют абстрактным классом. Поскольку в абстракт-
ном классе есть хотя бы одна функция, в которой отсутствует тело функции,
технически такой класс не является полностью определенным, и для него не-
возможно создать ни одного объекта. Поэтому абстрактные классы могут быть
только производными.
73
Основные концепции полиморфизма в языке программирования С++:
− полиморфизм - это свойство объекта изменять форму во время выпол-
нения программы;
− для создания методов, которые являются полиморфными, в программе
необходимо использовать виртуальные (virtual) функции;
− если объекты родительского (абстрактного) класса не предназначены
для реализации, необходимо ввести в класс хотя бы одну чисто виртуальную
функцию;
− любой класс, производный от базового, имеет возможность использо-
вать или перегружать виртуальные функции;
− для создания полиморфного объекта в С++ следует использовать указа-
тель на объект родительского класса;
− полиморфизм упрощает программирование, а создание полиморфных
объектов и уменьшает сложность программ.

Рассмотрим механизм полиморфизма на примерах.

Пример 3.1 Создан базовый класс и два класса-наследника. В базовом


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

Текст программы:
class TParent // базовый класс
{ public:
virtual void Draw();
};
class TChild1 : public TParent // класс− первый наследник
{ public: void Draw();
};
class TChild2 : public TParent // класс − второй наследник
{ public: void Draw();
};
void TParent:: Draw()// метод базового класса
{ Form1->Memo1->Lines->Add (" Это базовый класс"); }

void TChild1:: Draw()// метод класса − первого наследника


{ Form1->Memo1->Lines->Add (" Это 1-й производный класс"); }
74
void TChild2:: Draw()// метод класса − второго наследника
{ Form1->Memo1->Lines->Add (" Это 2-й производный класс "); }
//-----кнопка «Полиморфизм»------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{ TParent *ap[2];
ap[0]=new TParent;
ap[1]= new TChild1;
ap[2]= new TChild2;
for (int i=0;i<3; i++)
ap[i]->Draw();
}

Как видим, функция по имени Draw() является методом трех классов


TParent, TChild1, TChild2. Однако, вследствие свойства полиморфизма этой
функции, результаты её работы для объектов разных классов отличаются. Об-
ратите внимание на то, что объекты класса объявлены как указатели (*ap[2]),
иначе результаты будут другие. Рекомендуем поэкспериментировать. Форма
проекта с результатами выполнения программы имеет вид:

Пример 3.2 Создадим класс Piramid, описывающий правильную пирами-


ду. Этот класс создадим как абстрактный. Затем создадим производные от
класса Piramid классы: класс TrianPiramid, описывающий правильную тре-
угольную пирамиду, и класс TetrPiramid, описывающий правильную четырех-
угольную пирамиду.
В базовом классе опишем метод Sb(), вычисляющий площадь боковой по-
верхности пирамиды. Формула составлена таким образом, что она одна и та же
для любой пирамиды, т. к. использует периметр основания пирамиды и высоту
ее боковой грани (апофему). А вот последние для каждого вида пирамиды вы-
числяются по-разному. Поэтому объявим в базовом классе два виртуальных
метода − P() и Hb(), а в производных классах переопределим их как методы,
возвращающие соответственно периметр основания определенной пирамиды и
высоту ее боковой грани. Тогда для объектов каждого производного класса мы
сможем обратиться к методу Sb() базового класса. При вызове этого метода
компилятор будет знать, для объекта какого класса метод вызывается, и будет
сам выбирать соответствующую версию функций P() и Hb().
75
Описание классов выглядит следующим образом:

class Piramid // абстрактный базовый класс


{protected:
double a,h;
public:
Piramid():a(0),h(0){}
Piramid(double ap, double hp):a(ap),h(hp){}
virtual double P()=0;
virtual double Hbok()=0; // виртуальная функция абстрактного класса
double Sbok(){return P()*Hbok()/2;}
};
class TrianPiramid: public Piramid // первый потомок абстрактного класса
{
public:
TrianPiramid(double ap, double hp):Piramid(ap,hp){}
virtual double P(){return 3*a;}
double Hbok();
};

class TetrPiramid: public Piramid // второй потомок абстрактного класса


{
public:
TetrPiramid(double ap, double hp):Piramid(ap,hp){}
virtual double P(){return 4*a;}
double Hbok();
};

// реализация виртуальной функции первого потомка абстрактного класса


double TrianPiramid::Hbok()
{ double hp= a*sqrt(3)/2;
return sqrt(pow(hp/3,2)+pow(h,2));
}

// реализация виртуальной функции второго потомка абстрактного класса


double TetrPiramid::Hbok()
{
return sqrt(pow(a/2,2)+pow(h,2));
}

После описания классов можно создать приложение, демонстрирующее


работу с классами-наследниками TrianPiramid и TetrPiramid. Создадим два
объекта: tn типа TrianPiramid и tr типа TetrPiramid с помощью конструктора
с двумя параметрами. Параметры для обоих объектов вводит пользователь с
клавиатуры. Вызов метода Sbok() класса Piramid для этих объектов возвратит
нам площади боковых поверхностей треугольной и четырехугольной пирамид,
которые мы и выведем на экран.
76
Форма приложения имеет вид:

Код функции, выполняющейся по щелчку на кнопке «Расчет», имеет вид:


void __fastcall TForm1::Button1Click(TObject *Sender)
{
TrianPiramid tn(StrToFloat(Edit1->Text),
StrToFloat(Edit2->Text));
TetrPiramid tr(StrToFloat(Edit3->Text),
StrToFloat(Edit4->Text));
Edit5->Text=FloatToStr(tn.Sbok());
Edit6->Text=FloatToStr(tr.Sbok());
}
Результаты расчета для двух видов пирамид с одинаковыми по длине сторо-
нами основания и одинаковыми высотами показаны на форме проекта:

Пример 3.2 Построить класс 1-го уровня «Врач», с полями: «Фамилия


врача», «Количество пациентов», «Количество правильных диагнозов» и функ-
цией, определяющей качество по формуле:
Q=Количество правильных диагнозов / Количество пациентов.
77
Построить класс 2-го уровня как потомок класса 1-го уровня, который со-
держит дополнительное поле Р − «Количество лиц, которые после выздоровле-
ния вновь обратились к врачу» и перекрывает функцию качества Q новой фор-
мулой:
Qр=Q-2*P/ количество пациентов, если количество вновь обратившихся
к врачу больше 10 пациентов,
иначе Qр= Q-2*P/ количество пациентов.

В отдельных файлах описать классы как 1-го, так и 2-го уровня (размеще-
ние в отдельных файлах − см. пример 1.3), инициализировать их и вывести ин-
формацию о них на форму.

Текст программы (в пяти файлах)

1 Файл Unit2.h − описание класса 1-го уровня

#ifndef Unit2H
#define Unit2H
#include <vcl.h> // добавить библиотеку компонентов vcl.h
//-----------------------------------------------------------------------------------

class Tvrach {
private:
int kolpr;
AnsiString FIO;
protected:
int kolp;
virtual float rachest(void); // функция вычисления качества с перекрытием
public:
Tvrach (AnsiString iFIO, int ikolp, int ikolpr) ; // конструктор
AnsiString Info(void); // без перекрытия
};
#endif

2 Файл Unit2.cpp − реализация функций класса 1-го уровня


#pragma hdrstop
#include "Unit2.h"
//--------------------------------
#pragma package(smart_init)
//--------------------------------

Tvrach::Tvrach (AnsiString iFIO ,int ikolp, int ikolpr) // конструктор


{FIO=iFIO;kolp=ikolp; kolpr=ikolpr;}

float Tvrach::rachest() // функция расчета качества Q


{ return 1.0*kolpr/kolp; };
78
AnsiString Tvrach::Info() // функция формирования строки-информации s
{AnsiString s=FIO+" Q = "+FloatToStr(rachest());
return s;}

3 Файл Unit3.h − описание класса 2-го уровня


#ifndef Unit3H
#define Unit3H
#include "Unit2.h" // добавить модуль описания класса 1-го уровня (родителя)
//---------------------------------------------------------------------------

class TGvrach : public Tvrach


{private int kolV; //дополнительное поле потомка – объекта класса 2-го уровня
protected: float rachest(void); // уже без указания о перекрытии
public:
// конструктор потомка
TGvrach(AnsiString FIO, int ikolp, int ikolpr, int ikolV);
};

#endif

4 Файл Unit3.cpp − реализация функций класса 2-го уровня

#pragma hdrstop
#include "Unit3.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------

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


TGvrach::TGvrach(AnsiString FIO, int ikolp, int ikolpr, int ikolV):
Tvrach (iFIO, ikolp, ikolpr) // повторить конструктор класса 1-го уровня
{kolV=ikolV;} // и добавить к нему действия с доп. полем

float TGvrach::rachest() // описание функции rachest для класса 2-го уровня


{
float Q=Tvrach::rachest();// повторить формулы функции для класса 1-го уровня
if (kolV>10) // и добавить расчеты по формулам для класса 2-го уровня
return Q-2.0*kolV/kolp;
else
return Q-1.2*kolV/kolp;
}

5 Файл Unit1.cpp − главный модуль


#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "Unit2.h" // добавить модуль описания класса 1-го уровня
#include "Unit3.h" // добавить модуль описания класса 2-го уровня
79
//----------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//----------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{ }

//----------объявление переменных − указателей на объекты класса


Tvrach *p0, *p1;

//------------Кнопка «Вычислить качество»-------------------------------


void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString FIO= Edit1->Text;
int kolpac=StrToInt(Edit2->Text);
int kolviz=StrToInt(Edit3->Text);
p0=new Tvrach(FIO,kolpac,kolviz); //объект класса 1-го уровня
Memo1->Lines->Add(p0->Info());
int kolvi=StrToInt(Edit4->Text); //ввод количества обратившихся вновь
// для объекта класса 2-го уровня
p1=new TGvrach(FIO,kolpac,kolviz,kolvi); //объект класса 2-го уровня
Memo1->Lines->Add("измененное качество "+ p1->Info());
}

Форма проекта с результатами вычислений


80
Пример 3.3. Создадим класс list, котрый является базовым для стека и
очереди (см. пример 2.4). Преобразуем код программы таким образом, чтобы
можно было использовать свойство полиморфизма. Задача: ввести информацию
об озере: глубину для стека и солённость – для очереди. Для стека определить
самое глубокое озеро, а для очереди – самое пресное.
Форма проекта имеет вид:

Текст программы:

struct listec //описание динамической структуры с информацией об озере


{String Ozero;
int num;
listec *next;
};
class list //описание базового класса
{public:
listec *curr,*first,*last;
list(){curr=first=last=NULL;}
// виртуальная функция класса для добавления элемента в структуру
virtual void add(String ozero, int d);
AnsiString view(listec *fm);
listec *del(listec *fm);
// виртуальная функция класса для решения задачи
virtual AnsiString Zadacha(listec *fm);
};
class steck : public list //описание класса − стек
{ public:
void add(String ozero, int d);
AnsiString Zadacha(listec *fm);
};
81
class queue : public list //описание класса −очередь
{ public:
void add(String ozero, int d);
AnsiString Zadacha(listec *fm);
};

//реализация виртуальной функции добавления элемента в структуру


void list::add(String ozero, int d)
{ ShowMessage("Это базазовый класс для добавления"); }

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


AnsiString list::Zadacha(listec *fm)
{AnsiString s="";
ShowMessage("Это базазовый класс для задачи");return s;}

//реализация функции просмотра информации в структуре


AnsiString list::view(listec *fm)
{ curr=fm;
AnsiString s="";
if (curr==0) {ShowMessage("Нет ничего!");s="";return s;}
while (curr!=0)
{s=s+curr->Ozero+" "+IntToStr(curr->num)+"\r\n";
curr=curr->next;
}
return s; }

//реализация функции удаления элемента из структуры


listec *list::del(listec *fm)
{ curr=fm;
if (curr==0){ShowMessage("Нет ничего!");return fm=NULL;}
fm=fm->next;
delete curr;
return fm;
}

//реализация функции добавления элемента в стек


void steck::add(String ozero,int d)
{ curr=new listec;
curr->num=d;
curr->Ozero=ozero;
curr->next=last;
last=curr;
}

//реализация функции вычисления самого глубокого озера


AnsiString steck::Zadacha(listec *fm)
{ curr=fm;
AnsiString s="";
if (curr==0) {ShowMessage("Нет ничего!");s="";return s;}
82
int max=-100;
while (curr!=0)
{if (curr->num>max)
{max=curr->num;
s=curr->Ozero+" "+IntToStr(curr->num)+"\r\n";}
curr=curr->next;
}
return s; }

//реализация функции добавления элемента в очередь


void queue::add(String ozero,int d)
{ curr=new listec;
curr->num=d;
curr->Ozero=ozero;
curr->next=0;
if (first==0) first=curr; else last->next=curr;
last=curr;
}

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


AnsiString queue::Zadacha(listec *fm)
{ curr=fm;
AnsiString s="";
if (curr==0) {ShowMessage("Нет ничего!");s="";return s;}
int min=100;
while (curr!=0)
{if (curr->num<min)
{min=curr->num;
s=curr->Ozero+" "+IntToStr(curr->num)+"\r\n";}
curr=curr->next;
}
return s; }

//----------объявление переменных − указателей на объекты класса


list *ss[3];
//----------выделение памяти для динамических переменных (указателей)
void __fastcall TForm1::FormCreate(TObject *Sender)
{
ss[0]=new list;
ss[1]=new steck;
ss[2]=new queue;
}
//----------кнопка «Добавить в стек»-------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{ String ozero=Edit1->Text;;
int d=StrToInt(Edit2->Text);
ss[1]->add(ozero,d);
Edit1->Text="";
83
Edit2->Text="";
Edit1->SetFocus();
Button2Click(0);
}
//----------кнопка «Удалить из стека»-------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{ ss[1]->last=ss[1]->del(ss[1]->last);
Button2Click(0);
}
//----------кнопка «Просмотр» (для стека)---------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{ Memo1->Clear();
Memo1->Lines->Add(ss[1]->view(ss[1]->last));
}
//----------кнопка «Задача для стека»-------------------------
void __fastcall TForm1::Button8Click(TObject *Sender)
{ Memo1->Lines->Add(ss[1]->Zadacha(ss[1]->last)); }
//----------кнопка «Добавить в очередь»-------------------------
void __fastcall TForm1::Button5Click(TObject *Sender)
{ String ozero=Edit1->Text;;
int d=StrToInt(Edit2->Text);
ss[2]->add(ozero,d);
Edit1->Text="";
Edit2->Text="";
Edit1->SetFocus();
Button6Click(0);
}
//----------кнопка «Просмотреть» (для очереди)---------------------
void __fastcall TForm1::Button6Click(TObject *Sender)
{ Memo2->Clear();
Memo2->Lines->Add(ss[2]->view(ss[2]->first));
}
//----------кнопка «Удалить из очереди»-------------------------
void __fastcall TForm1::Button7Click(TObject *Sender)
{ ss[2]->first=ss[2]->del(ss[2]->first);
Button6Click(0);
}
//----------кнопка «Задача для очереди»-------------------------
void __fastcall TForm1::Button9Click(TObject *Sender)
{ Memo2->Lines->Add(ss[2]->Zadacha(ss[2]->first)); }

В приведенном коде в базовом классе представлены методы, два из кото-


рых объявлены как виртуальные. Если в базовом классе убрать спецификатор
virtual, то замещение этих методов из наследуемых классов происходить не
будет, что будет отображаться в виде сообщения, что работает базовый класс.
84

Л а б о р а т о р н а я р а б о т а № 2.3

Программная реализация свойства полиморфизма

Цель работы. Изучить понятия и принципы полиморфизма в объектно-


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

Контрольные вопросы

1 Что такое полиморфизм? Приведите примеры.


2 Какие условия должны быть выполнены, чтобы использовать полимор-
физм функций в классах?
3 Что означает спецификатор virtual?
4 Нужно ли писать спецификатор virtual при объявлении функции в
производном классе?
5 Какая функция называется чисто виртуальной?
6 Что такое абстрактный класс?
7 Что такое полиморфный объект?

Лабораторное задание

1 Изучить теретический материал и дать ответы на контрольные вопросы.


2 Составить программы на языке С++ в соответствии с индивидуальным
заданием для базового, среднего и высокого уровней.
3 Оформить протокол лабораторной работы.
4 Выполнить программы на компьютере и записать результаты их выпол-
нения.
1 Базовый уровень

Построить класс 1-го уровня с указанными в индивидуальном задании


(табл. 3.1) полями и методами:
- конструктор,
- функция, которая определяет “качество” объекта – Q по заданной фор-
муле (табл. 3.1, столб. 2),
- вывод информации об объекте.
Построить класс 2-го уровня (класс-потомок), который содержит:
- дополнительное поле P;
- функцию, которая определяет “качество” объекта класса 2-го уровня –
Qp и которая перекрывает функцию качества класса 1-го уровня (Q ), выполняя
вычисление по новой формуле (табл. 3.1, столб. 3).
Создать проект для демонстрации работы: ввод и вывод информации об
объектах классов 1-го и 2-го уровней.
85
Таблица 3.1 – Варианты индивидуальных заданий

№ Поля и функция “качества“ (Q) Поле и функция “качества“


вар. класса 1-го уровня Qp класса 2-го уровня
1 Мобильный телефон: P: количество SIM карт
- марка,
- цена, Qp = Q*Р
- объем памяти.
Q = объем памяти / цена
2 Спутниковая антенна (тарелка): P: тип подвески (азимуталь-
- диаметр, ная, полярная, тороидальная)
- материал,
- цена. Qp= Q, если тип подвески ази-
мутальный,
Q = диаметр / цена Qp = 2*Q, если тип подвески
полярный,
Qp = 2,5*Q, если тип подвески
тороидальный
3 Экзамен: P: процент двоек
- дисциплина,
- число студентов на экзамене,
- продолжительность экзамена (ч.). Qp = Q*( 100−Р)/100
Q = число студентов / продолжитель-
ность
4 Спортсмен: P: занимал ли хотя бы раз пер-
- фамилия, вое место
- число соревнований,
- сумма мест, занятых спортсменом в Qp = 1,5*Q, если Р - истина,
соревнованиях иначе – Qp= Q,
Q = (число соревнований)/ (сумма мест)
5 Программист: P: число программ, которые
- фамилия, работают правильно
- число программ, написанных про-
граммистом,
- число языков программирования, Qp = Q * Р / (число всех про-
которыми он пишет программы. грамм)
Q = (число программ)*(число языков)
6 Компьютер: P: объем винчестера (Гб)
- наименование процессора,
-тактовая частота процессора (Мгц), Qp=Q+0,5*Р
- объем оперативной памяти (Мб).
Q = (0,1*частота) + память
86
№ Поля и функция “качества“ (Q) Поле и функция “качества“
вар. класса 1-го уровня Qp класса 2-го уровня
7 Оператор мобильной связи: P: наличие платы за каждое
- название оператора, соединение
- стоимость 1 минуты разговора,
- площадь покрытия. Qp = 0,7*Q, если Р - истина,
Q = 100 * площадь покрытия / стоимость иначе Qp = 1,5*Q
1 минуты разговора
8 Товар на складе: P: год выпуска товара
- наименование,
- цена, Qp = Q + 0,5*(Т–Р),
- количество. где Т - текущий год
Q = цена / количество
9 Кабель: P: наличие оплетки
- тип,
- количество жил кабеля, Qp: если Р - истина,
- диаметр. то Qp = 2*Q;
Q = диаметр / количество жил иначе Qp = 0,7*Q
10 Учебник по программированию: P: год издания
- название,
- количество страниц, Qp = Q – 0,2*(Т–Р),
- цена. где Q - текущий год
Q = цена / количество страниц
11 Спектакль: P: год написания пьесы
- название,
n1 – число зрителей в начале, Qp = Q * (Т−Р+1),
n2 – число зрителей в конце. где Т - текущий год
Q = (n2 – n1)/ n1
12 Алмаз: P: цвет (белый, голубой, жел-
- название, тый, и т.п.)
- вес (в каратах), Qp: если цвет голубой,
- качество огранки в баллах (число). то Qp=Q+1;
Q = 0,4*вес + 0,6*качество огранки а если желтый,
то Qp = Q – 0,5
иначе Qp=Q
13 Компьютерная сеть: P: средняя скорость передачи
- название организации, данных в сети (Мб/с)
- число рабочих станций,
- среднее расстояние между станциями Qp = Q*Р
(м).
Q = число станций*среднее расстояние
87
№ Поля и функция “качества“ (Q) Поле и функция “качества“
вар. класса 1-го уровня Qp класса 2-го уровня
14 Армия: P: опыт (число месяцев, на
- вид войск, протяжении которых армия
- численность (тыс. человек), вела боевые действия)
- вооруженность (баллы - число). Qp = Q * (Р+1)
Q=0,3*численность+0,7*вооруженность
15 Автомобиль: P: год изготовления
- марка автомобиля,
- мощность двигателя (кВт), Qp=Q−1,5*(Т−Р) ,
- число мест. где Т - текущий год

Q = 0,1*мощность*число мест
16 Студент: P: число оценок «три»
- фамилия,
- число экзаменов,
- число оценок «пять». Qp = Q − 0,5*Р
Q = число оценок «пять» / число экзаме-
нов
17 Фирма: P: инвестиции в фирму (тыс. $)
- название,
- доход (тыс. $ ), Qp = Р3 + Q
- рейтинг (в баллах).
Q = доход *рейтинг
18 Военный корабль: P: крейсерская скорость
- название, (в морских узлах)
- длина,
- число пушек главного калибра. Qp = 0,25*Q + Р
Q = (число пушек) / длина
19 Коробка спичек: P: средний % бракованных
- фирма изготовитель, спичек в коробке
- число спичек в коробке,
- время горения одной спички (с). Qp = ( 100−Р) * Q / 100
Q = (число спичек)*время

20 Полководец: P: число побед с меньшими,


- фамилия, чем у противника, силами
- число битв,
- число побед. Qp = Р2/битвы +Q
Q = (число побед)2/(число битв)
88
№ Поля и функция “качества“ (Q) Поле и функция “качества“
вар. класса 1-го уровня Qp класса 2-го уровня
21 Партия: P: численность партии в про-
- название, шлом году
- численность (тыс. членов),
- процент голосов на последних выбо- Qp: если численность в теку-
рах. щем году увеличилась, то
Qp=1,2*Q;
Q = 0,3*численность+0,7*процент гол. а если сократилась, то
Qp=0,8*Q
22 Высшее учебное заведение: P: процент выпускников, ко-
- название заведения, торые работают по специаль-
- количество студентов, зачисленных ности
на 1-й курс,
- количество выпускников. Qp = Р*Q
Q = количество выпускников / количе-
ство зачисленных
23 Солдат: P: образование (начальное,
- фамилия, среднее, высшее)
- рост (м), Qp: если образование высшее,
- вес (кг). то Qp=2*Q; а если начальное,
Q = рост*вес то Qp=0,5*Q; иначе Qp=Q
24 Телевизор: P: страна-производитель
- фирма, Qp: если страна - Япония, то
- диагональ экрана (дюйм), Qp=2*Q;
- звуковая мощность (дб). а если Сингапур или Корея, то
Q = диагональ+(0,05*мощность) Qp=1,5*Q; иначе Qp=Q
25 Митинг: P: число групп ораторов, ко-
- название события, торые высказывали одинако-
n1 – число ораторов, вые мысли
n2 – число участников.
Q = n1/n2 Qp = Q + Р/n2
26 Дом: P: район (центр, окраина, и
- номер дома, т.п.)
- число квартир,
- год сооружения. Qp: если район - центр, то
Q=(число квартир)+2*(Т – Qp=2*Q;
– год сооружения), иначе Qp=0,5*Q
где Т - текущий год
27 Руководитель: P: оценка потомками (в бал-
- фамилия, лах)
- самооценка (в баллах - целое число),
- оценка другими людьми (в баллах).
Q = (оценка другими)/самооценка Qp = 0,3*Q + 0,7*Р
89
№ Поля и функция “качества“ (Q) Поле и функция “качества“
вар. класса 1-го уровня Qp класса 2-го уровня
28 Студент: P: изучает дисциплины на
- фамилия, английском языке
- средний балл,
- курс. Qp = 2*Q, если Р - истина,
Q = 0,2 * средний балл*курс иначе Qp = 0,9*Q
29 Антенна: P: коэффициент излучения
- название,
- мощность, Qp = Q − 0,1*Р
- высота (м).
Q = мощность +0,5*высота
30 Самолет: P: страна-производитель
- марка,
- количество двигателей, Qp: если страна - Россия, то
- высота полета. Qp=Q+1;
а если Франция, то Qp=Q+0,5
Q = кол-во двигателей * высота полета
/1000

2 Средний уровень

Написать программу согласно заданию. Во всех классах описать необхо-


димые конструкторы, при помощи которых будут создаваться объекты классов.
Параметры создаваемых объектов задавать константами или вводить с клавиа-
туры или формы; передавать в конструкторы объектов в виде параметров. Вы-
вод информации должен осуществляться либо на консоль или в многострочное
текстовое поле (Memo).

Задание
вар
Создать класс Самолет со свойствами: Марка, Модель, Максимальная
скорость (в км/ч), Максимальная высота (в метрах). Определить виртуаль-
ный метод «Стоимость» – стоимость самолета, рассчитываемую по фор-
муле Максимальная скорость * 1000 + Максимальная высота * 100. Опре-
делить также метод «Информация», который возвращает строку, содер-
жащую информацию об объекте: Марка, Модель, Максимальную ско-
1 рость, Максимальную высоту и Стоимость.
Создать также класс наследник Бомбардировщик, в котором переопреде-
лить метод «Стоимость» который вернет удвоенную стоимость относи-
тельно формулы для класса Самолет. Также создать класс Истребитель –
наследник класса Самолет, для которого переопределить метод «Стои-
мость» как утроенную стоимость, относительно формулы стоимости для
Самолета.
90
В главной программе (либо по нажатию на кнопку) создать объекты класса
Самолет, класса Бомбардировщик, класса Истребитель. Вывести на эк-
ран (или форму) информацию о самолетах.
Создать класс Компьютер со свойствами: Частота процессора (в МГц),
количество ядер, объем памяти (в МБ), объем жесткого диска (в ГБ). Оп-
ределить два виртуальных метода: «Стоимость», возвращающую пример-
ную расчетную стоимость компьютера, рассчитываемую по формуле Час-
тота процессора * количество ядер / 100 + количество памяти / 80 + объем
жесткого диска / 20 и логический метод «Пригодность», возвращающий
истину (true), если частота процессора не менее 2000 МГц, количество
ядер не менее 2, объем памяти не менее 2048 МБ, и объем жесткого диска
не менее 320 Гб. Определить также метод «Информация», который воз-
вращает строку, содержащую информацию о компьютере: частоту процес-
сора, количество ядер, объем памяти, объем жесткого диска, стоимость и
2
пригодность для наших нужд.
Создать также класс наследник Ноутбук, с дополнительным свойством
Продолжительность автономной работы (в минутах) и переопределить ме-
тоды: метод «Стоимость» возвращает число, равное стоимости обычного
компьютера + количество минут автономной работы / 10, а метод «При-
годность» возвращает истину, тогда когда и ноутбук пригоден как обыч-
ный компьютер, и Продолжительность автономной работы не меньше 60
минут.
В главной программе (либо по нажатию на кнопку) создать обычный ком-
пьютер и ноутбук и вывести информацию о них.
Создать класс Фотоаппарат со свойствами: Модель, Оптическое увели-
чение (Zoom, вещественное число от 1 до 35) и материал корпуса (металл
либо пластик). Определить виртуальный метод: метод «Стоимость» – воз-
вращает число – стоимость фотоаппарата (в $), рассчитываемую по фор-
муле (Zoom+2) * 10, если корпус пластиковый и (Zoom+2)*15, если мате-
риал металлический. Определить также метод «Информация», который
возвращает строку, содержащую информацию об объекте: Модель, Zoom и
Стоимость. Также определить логический метод «Дорогой», который бу-
дет возвращать истину (true), если стоимость фотоаппарата больше 200$.
3
Создать также класс наследник Цифровой фотоаппарат, в котором будет
дополнительный целый параметр – количество мегапикселей и переопре-
делить метод «Стоимость», который будет возвращать число, равное
Стоимости обычного фотоаппарата умножить на количество мегапиксе-
лей, а также определить новый метод «Обновление модели», который уве-
личивает количество мегапикселей на 2.
В главной программе (либо по нажатию на кнопку) создать объект класса
Фотоаппарат с 4-х кратным оптическим увеличением (Zoom=4) и пласти-
ковым корпусом, а также Цифровой фотоаппарат с металлическим кор-
91
пусом, 8-ю мегапикселями и 3-кратным оптическим увеличением. Вывес-
ти на экран (или форму) информацию о фотоаппаратах и о том, являются
ли они дорогими. Обновить модели цифрового фотоаппарата и снова вы-
вести информацию о нем
Создать класс Круг заданный своим радиусом (r), с виртуальным методом
«Площадь», возвращающим площадь круга, а также виртуальный метод
«Увеличить» с одним вещественным параметром – во сколько раз увели-
чить, увеличивающий радиус в заданное число раз. Определить также ме-
тод «Информация», который возвращает строку, содержащую информа-
цию о круге: радиус и площадь.
Создать также класс наследник Кольцо, с дополнительным параметром ––
4 внутренним радиусом (rin), при этом унаследованный от родителя радиус
будет обозначать внешний радиус. Переопределить метод «Площадь», как
разницу между площадью внешнего круга минус площадь внутреннего
круга. Также доопределить метод «Увеличить», чтобы он увеличивал так-
же и внутренний радиус.
В главной программе (либо по нажатию на кнопку) создать обычный круг
и кольцо и вывести информацию о них. После этого увеличить оба объек-
та в полтора раза и выдать обновленную информацию.
Создать класс Автомобиль со свойствами: Название, Максимальная ско-
рость (в км/ч). Определить 2 виртуальных метода: метод «Стоимость» –
стоимость автомобиля, рассчитываемую по формуле Максимальная ско-
рость * 100 и метод «Обновление модели», увеличивающий максималь-
ную скорость на 10. Определить также метод «Информация», который
возвращает строку, содержащую информацию об объекте: Название, Мак-
симальную скорость и Стоимость.
Создать также класс наследник Представительский автомобиль, в кото-
5
ром переопределить методы: метод «Стоимость» возвращает число, равное
Максимальная скорость * 250, а метод «Обновление модели» увеличивает
скорость на 5 км/ч.
В главной программе (либо по нажатию на кнопку) создать объект класса
Автомобиль с максимальной скоростью 140 км/ч и класса Представи-
тельский автомобиль с максимальной скоростью 160 км/ч. Вывести на
экран (или форму) информацию об автомобилях. Обновить модели авто-
мобилей и снова вывести информацию о них.
Создать класс Треугольник, заданный значениями длин трех сторон (a, b,
c), с методами «Периметр» и «Площадь». Определить также метод «Ин-
формация», который возвращает строку, содержащую информацию о тре-
6 угольнике: длины сторон, периметр и площадь.
Создать также класс наследник Четырехугольник, с дополнительными
параметрами – длиной четвертой стороны (d) и длинами диагоналей (e, f) и
переопределить методы «Периметр» (сумма всех сторон) и «Площадь».
92

Площадь вычислять по следующей формуле S =


(
4e 2 f 2 − b 2 + d 2 − a 2 − c 2 )
2
.
16
В главной программе (либо по нажатию на кнопку) создать объект класса
Треугольник и объект класса Четырехугольник и вывести информацию
о них. Для упрощения проверки рекомендуется в качестве конкретного
объекта класса четырехугольник взять квадрат.
Создать класс Прямоугольник, заданный значениями длин двух сторон (a
и b), с виртуальными методами «Периметр» и «Площадь», возвращающи-
ми периметр и площадь соответственно, а также виртуальный метод «Уве-
личить в два раза», увеличивающий в два раза каждую из сторон. Опреде-
лить также метод «Информация», который возвращает строку, содержа-
щую информацию об треугольнике: длины сторон, периметр и площадь.

Создать также класс наследник Прямоугольник со скругленными угла-


7 ми, с дополнительным параметром радиус скругления (r). Для него пере-
определить Периметр по формуле p – 8·r + 2·π·r, где p – периметр обычно-
го прямоугольника с теми же сторонами, а Площадь по формуле
S – 4·r2 + π·r2, где S – площадь обычного прямоугольника. Также переоп-
ределить метод «Увеличить в два раза» так, чтобы он также увеличивал в
два раза радиус скругления (по-прежнему увеличивая стороны в два раза).
В главной программе (либо по нажатию на кнопку) создать обычный пря-
моугольник и прямоугольник со скругленными углами и вывести ин-
формацию о них. После этого увеличить оба прямоугольника в два раза и
выдать обновленную информацию.
Создать класс Студент со свойствами: ФИО, Факультет, Курс, минималь-
ная оценка по экзаменам за последнюю сессию (по 5-ти бальной системе).
Определить виртуальные методы: «Перевести на следующий курс», уве-
личивающий курс на 1, если минимальная оценка не менее 3, иначе не де-
лающий ничего, а также «Стипендия», возвращающий стипендию (в грн):
0 грн, если минимальная оценка не выше 3, 200 грн, если минимальная
оценка равна 4 и 300 грн, если минимальная оценка равна 5. Определить
8 также метод «Информация», который возвращает строку, содержащую
информацию о студенте: ФИО, Факультет, Курс, минимальная оценка по
экзаменам и начисленную стипендию.
Создать также класс наследник Студент-контрактник, в котором будет
дополнительный логический параметр – уплачен ли контракт и переопре-
делены методы «Перевести на следующий курс», увеличивающий курс на
1, если минимальная оценка не менее 3 и за контракт уплачено, а также
«Стипендия» возвращающий всегда 0 грн.
93
В главной программе (либо по нажатию на кнопку) создать объект класса
Студент и 2 объекта класса Студент-контрактник (один из которых уп-
латил за контракт, а другой нет). Выдать информацию о студентах, затем
применить к ним метод «Перевести на следующий курс» и снова выдать
информацию о них.
Создать класс Табуретка со свойствами: Высота (h, в см.), Качество изде-
лия (низкое, среднее, высокое). Определить два виртуальных метода: «ко-
личество древесины», которое требует табуретка, по формуле 4*h+12, если
качество низкое, и 5*h+14, если качество среднее или высокое, а также
«стоимость», равная d*2, для низкого качества, d*3, для среднего качест-
ва, d*4, для высокого качества, где d – количество древесины, которое
требует данный объект. Определить также метод «Информация», который
возвращает строку, содержащую информацию об объекте: Высоту, качест-
9 во материала, количество древесины и стоимость.
Создать также класс наследник Стул с дополнительным свойством: высо-
та спинки (h2, в см.), и переопределить метод «количество древесины», по
формуле d+2*h2+5, где d – количество древесины, которые требует табу-
ретка с такими же параметрами. (Метод «стоимость» не переопределять).
В главной программе (либо по нажатию на кнопку) создать экземпляры
классов Табуретка и Стул, и напечатать информацию в таком виде: «та-
буретка» + информация о табуретке и «стул» + информация о стуле.
Создать класс Фильм со свойствами: Название, Режиссер, длительность (в
минутах), количество актеров. Определить виртуальный метод: «Стои-
мость», возвращающую примерную расчетную стоимость фильма (в тыс.
$), рассчитываемую по формуле длительность*20 + количество акте-
ров*30, но если режиссер = «Стивен Спилберг» или «Джеймс Кэмерон»,
то стоимость в два раза выше (по сравнению с вышеуказанной формулой).
Определить также метод «Информация», который возвращает строку, со-
держащую информацию о фильме: Название, режиссера, длительность,
10
количество актеров и стоимость.
Создать также класс наследник Мультфильм, в котором переопределить
метод «Стоимость» по формуле длительность*25 + количество актеров*10
(вне зависимости от режиссера).
В главной программе (либо по нажатию на кнопку) создать 2 фильма с
режиссерами: «Стивен Спилберг» и «Ежи Гофман», а также мультфильм
и вывести информацию о них.
94

4 КЛАССЫ И ОБЪЕКТЫ БИБЛИОТЕКИ


ВИЗУАЛЬНЫХ КОМПОНЕНТ

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

В среде C++ Builder компоненты (например: Button, StringGrid, Edit и т.п.)


являются специальными классами, которые сформированы в библиотеке систе-
мы Visual Components Library (VCL). Классы компонентов создаются разра-
ботчиками программного обеспечения и используются программистами при
работе в интегрированной среде программирования С++ Builder. Каждый класс
компонентов состоит из следующих элементов: свойства (properties), методы
(methods) и события (events).
Свойства компонентов − это дополнительные поля класса, которые обес-
печивают доступ к данным класса не только в программе, но и при создании
формы проекта (например: Caption, Top, Left, Font т.п.). Методы класса
реализуют определенное стандартное поведение компонентов (например:
Show(), Hide(),SetFocus()и т.д.). События − это специальные методы класса,
с помощью которых компонент извещает пользователю о том, что над ним вы-
полнили какое-то действие и будет выполнен программный код, предусмотрен-
ный программистом (например: Click, MouseDown, KeyPress т.п.). Перечень
всех элементов компонентов можно просмотреть с помощью справочной сис-
темы Help.
Классы компонентов, как принято в объектно-ориентированном програм-
мировании, имеют иерархическую структуру. Приведем фрагмент дерева клас-
сов библиотеки визуальных компонентов C++ Builder (рис. 4.1).
Класс TObject − это базовый класс, который является общим предком всех
классов. Он обеспечивает следующие методы: способность конструктора соз-
давать, а деструктора уничтожать объект класса в динамической памяти, обра-
ботки сообщений Windows и т.д.К методам этого класса не следует обращаться
со своих программ, поскольку они могут быть переопределен в производных
классах.
Класс TPersistent – производный класс от TObject.Это абстрактный
класс, который определяет ряд методов копирования объектов подобных клас-
сов, возвращение владельца данного объекта, загрузка и сохранение специаль-
ной информации.
Класс TComponent – производный класс от TPersistent и родительский
класс для всех компонентов.Э тот класс обеспечивает отображение компонен-
тов и манипуляцию с ними в редакторе форм. Не следует создавать объекты
этого класса в своих программах, но можно использовать этот класс для созда-
ния производных классов. Производными от этого класса являются классы не-
визуальных компонентов (например: TMainMenu, TTimer, OpenDialog др.), ко-
торые изображаются как пиктограммы на форме во время проектирования, но
их не видно во время исполнения.
95

TObject

TPersistent

TComponent

TControl TMainMenu TTime …

TGraphicControl TWinControl TLabel …

TImage … TEdit TMemo TStringGrid TButton …

Рисунок 4.1 − Иерархия классов библиотеки VCL


(курсивом обзначены классы компонентов)
Класс TControl – базовый класс всех визуальных компонентов.Этот класс
устанавливает свойства компонента: расположение, размеры, видимость, дос-
тупность, цвет, шрифт и т.д.
В табл. 4.1 приведены стандартные заголовки и назначение некоторых ме-
тодов, присущих объектам класса TControl, а значит, и всем потомкам этого
класса.

Таблица 4.1 − Заголовки некоторых методов объектов класса Tcontrol

DYNAMIC void __fastcall Генерирует сообщение о щелчке


Click(void); левой кнопкой мыши
DYNAMIC void __fastcall Сообщение о двойном щелчке
DblClick(void); левой кнопкой мыши
DYNAMIC void __fastcall Сообщение о нажатии кнопки мыши.
MouseDown(TMouseButton Button, Параметр Button определяет нажатие:
Classes::TShiftState Shift, левой, средней или правой кнопки
int X, int Y); мыши. Shift показывает, была ли при
этом нажата какая-нибудь из кнопок
клавиатуры: Shift, Ctrl, Alt. X, Y – ко-
ординаты мыши
DYNAMIC void __fastcall Сообщение о том, что мышь движется.
MouseMove(Classes::TShiftState Параметры аналогичны приведенным
Shift, int X, int Y); выше
96
DYNAMIC void __fastcall MouseUp( Сообщение об отпускании кнопки
TMouseButton Button, мыши. Параметры аналогичны пара-
Classes::TShiftState Shift, метрам метода MouseDown
int X, int Y);
DYNAMIC void __fastcall Re- Сообщение о том, что размеры объек-
size(void); та изменились

В табл. 4.2 приведены заголовки некоторых методов класса TWinControl


(обратите внимание на директиву virtual в объявлении функции SetFocus −
роль этой директивы подобна директиве DYNAMIC):

Таблица 4.2 − Заголовки некоторых методов класса TwinControl


virtual void __fastcall Передает объекту “фокус”, после че-
SetFocus(void); го нажатия клавиш клавиатуры будут
восприниматься именно этим объек-
том
DYNAMIC void__fastcall KeyDown( Генерирует сообщение о нажатии
Word &Key, клавиши Key. Параметр Shift такой
Classes::TShiftState Shift); же, как в MouseDown
DYNAMIC void__fastcall KeyUp( Сообщение об отпускании клавиши
Word &Key, Key. Параметр Shift такой же,
Classes::TShiftState Shift); как в MouseDown и KeyDown
DYNAMIC void__fastcall KeyPress( Сообщение о том, что клавиша Key
char &Key); была нажата и отпущена. В отличие
от методов KeyDown и KeyUp, здесь
Key – не код (номер) клавиши, а сим-
вол, который ей соответствует. Кла-
виши, с которыми не связаны симво-
лы (такие как Enter или F1), не обра-
батываются методом KeyPress

4.2 Методика создания компонента пользователя

С++ Builder позволяет программисту создавать свой собственный компо-


нент, поместить его на одну из вкладок палитры компонентов и использовать
его при разработке приложения. При создании компонентов создаются произ-
водные классы, которые наследуют базовые классы, но с некоторыми особен-
ностями, характерными для библиотеки компонентов C++ Builder. К этим осо-
бенностям следует отнести невозможность для таких классов множественного
наследования и необходимость создавать объекты только с помощью операции
new. Кроме того, вводится понятие, связанное со свойствами и событиями.
Понятие свойства (property) объединяет поля данных и функции (методы)
его записи и чтения. В рассматриваемых классах сами поля объявляются как
обычно, но как правило, в разделе private. Традиционно идентификаторы по-
97
лей совпадают с именами соответствующих свойств, но с добавлением в каче-
стве префикса символа 'F'.
Свойства объявляются оператором вида:
__property <тип> = ( read=<имя поля или метода чтения>
write=<имя поля или метода записи>
<директивы запоминания и значение по умолчанию> );

Если в разделах read или write этого объявления записано имя поля, зна-
чит предполагается прямое чтение или запись данных.
Если в разделе read записано имя метода чтения, то чтение будет осуще-
ствляться только функцией с этим именем. Имя функции чтения принято начи-
нать с префиксом Get, после которого следует имя свойства.
Если в разделе write записано имя метода записи, то запись будет осуще-
ствляться только процедурой с этим именем. Имя процедуры записи принято
начинать с префикса Set, после которого следует имя свойства.
Если раздел write отсутствует в объявлении свойств, значит это свойство
только для чтения. Для директивы запоминания чаще всего используется ди-
ректива следующего вида:
default <значение по умолчанию>

Пример 4.1 Пусть требуется объявить класс с именем MyClass, наследую-


щий непосредственно TObject и имеющий свойство целого типа с именем А.
Тогда объявление этого класса может иметь вид:
class MyClass1 : public Tobject
{
private:
int FA;
protected:
void __fastcall SetA(int); // функция записи
published:
__property int A = {read=FA, write = SetA, default = true};
};

Здесь вводится закрытое поле FA, объявляется защищенная функция SetA,


используемая для записи значения в это поле, и вводится опубликованное свой-
ство A, оперирующее этим полем. В объявлении свойства после ключевого сло-
ва read записано просто имя поля. Это означает, что функция отсутствует и
пользователь может читать непосредственно значение поля. После ключевого
слова write следует ссылка на функцию записи SetA, с помощью которой будет
записываться в поле А новое значение. В этой функции можно предусмотреть
какие-то проверки допустимости вводимого значения А.
Описание этой функции может иметь вид:
98
void __fastcall MyClass::SetA(int Value)
{
if (…) FA=Value;
}

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


тельность следующих этапов:
1. Выбор базового компонента;
2. Создание модуля компонента;
3. Тестирование компонента;
4. Добавление компонента в пакет компонентов.

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


рассмотрением только первых трех пунктов; как добавить новый компонент на
палитру, можно ознакомиться в учебниках [5,6]. Наиболее просто создавать
компонент программиста можно на базе существующего (базового) компонента
путем расширения или ограничения возможностей базового компонента. На-
пример, компонент, обеспечивающий ввод и редактирование числа, логично
создать на основе компонента, обеспечивающего ввод строки символов, т.е. на
базе существующего компонента Еdit.
Рассмотрим процесс создания компонента программиста на примере.

Пример 4.2 Создадим компонент Ed, который позволяет вводить только


цифровую информацию (обычный компонент Еdit позволяет вводить любые
символы).
Порядок создания нового компонента в С++ Builder:
1) Перед началом работы по созданию нового компонента нужно создать
отдельную папку для модуля компонента и других файлов.
2) Найти в главном меню С++ Builder заголовок Component и выполнить
команду New/Component. В появившемся диалоговом окне ввести информа-
цию о создаваемом компоненте.
99
В поле Ancestor type ввести базовый тип создаваемого компонента (в
нашем примере таким компонентом является стандартный компонент Edit).
В поле Class Name необходимо ввести имя класса разрабатываемого ком-
понента, например TEd.
В поле Palette Page нужно ввести имя закладки палитры компонентов
(так как мы устанавливать компонент не будем, то можно оставить предлагае-
мый вариант по умолчанию без изменения).
В поле Unit file name − имя файла модуля, в котором записывается код
программы на языке C++, необходимый для построения нового класса,. Следу-
ет указать имя этого модуля (полный путь к нему). По умолчанию имя файлов
модуля совпадает с именем класса, за исключением первой буквы T, следова-
тельно, если класс называется TEd, файлы будут названы Ed.cpp и Ed.h. Что ка-
сается места расположения файла, то по умолчанию C++ Builder устанавливает
путь к своему библиотечному каталогу LIB. Чтобы не “засорять” каталог LIB,
рекомендуется изменить этот путь и разместить модуль нового класса в собст-
венном каталоге, например:
C:\STUDENTS\SEMESTR2\A24\<фамилия>\Ed.cpp
В поле Seach path можно указать папку, в которой будет храниться созда-
ваемый модуль и этот путь будет совпадать по умолчанию с папкой, которая
была выбрана в поле Unit file name.
3) После заполнения всех полей можно выполнить щелчок на кнопке Ok, и
в результате будет сформирован модуль компонента, состоящий из двух фай-
лов: файла заголовка Ed.h и файла реализации Ed.cpp.

Файл Ed.h − стандартный


#ifndef EdH
#define EdH
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
class PACKAGE TEd : public TEdit
{
private:
protected:
public:
__fastcall TEd(TComponent* Owner);
__published:
};
#endif

Файл Ed.cpp − стандартный


#include <vcl.h>
#pragma hdrstop
100
#include "Ed.h"
#pragma package(smart_init)
// ValidCtrCheck is used to assure that the components created do
not have
// any pure virtual functions.
//
static inline void ValidCtrCheck(TEd *)
{
new TEd(NULL);
}
__fastcall TEd::TEd(TComponent* Owner)
: TEdit(Owner)
{
}
namespace Ed
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TEd)};
RegisterComponents("Samples", classes, 0);
}
}

В файле заголовка Ed.h находится объявление нового класса. В файл реа-


лизации Ed.cpp помещена функция Register(), которая обеспечивает регистра-
цию компонента и установку значка компонента на указанную закладку палит-
ры компонентов.
4) В сформированный шаблон компонента необходимо ввести дополнения:
объявить поля данных, функции доступа к полям, свойства и методы. Если на
некоторые события компонент должен реагировать не так как базовый, то в
объявлении класса нужно поместить объявление соответствующих функций
обработки событий.
Ниже приводятся соответствующие программные коды для нового класса,
в соответствии с поставленной задачей (добавленные элементы класса выделе-
ны полужиром):

Файл Ed.h − измененный

#ifndef EdH
#define EdH
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
101
class PACKAGE TEd : public TEdit
{
private:
bool FEnabfloat;
void __fastcall SetNumb(float f);//для изменения содержимого TEd
float __fastcall GetNumb(void); // для ввода данных в TEd
protected:
public:
__fastcall TEd(TComponent* Owner);

//свойство Numb (доступное только во время работы программы).


__property float Numb = {read=GetNumb, write=SetNumb};
DYNAMIC void __fastcall KeyPress(char &Key);
__published:
};
#endif
Файл Ed.cpp
#include <vcl.h>
#pragma hdrstop
#include "Ed.h"
#pragma package(smart_init)
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//
static inline void ValidCtrCheck(TEd *)
{
new TEd(NULL);
}
__fastcall TEd::TEd(TComponent* Owner)
: TEdit(Owner)
{ FEnabfloat=true; Text=""; }
namespace Ed
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TEd)};
RegisterComponents("Samples", classes, 0);
}
}
// рализация функции чтения вводимого символа с клавиатуры
void __fastcall TEd::SetNumb(float f)
{ Text=FloatToStr(f);
}
// рализация функции записи символа в компонент TEd
float __fastcall TEd::GetNumb(void)
{ if (Text.Length()) return StrToFloat(Text);
else return 0;
}
102
// функция KeyPress, измененная в соответствии с заданем
void __fastcall TEd::KeyPress(char &Key)
{switch (Key)
{case '0': case '1': case '2': case '3': case '4': case '5': case '6':
case '7': case '8': case '9': break;
case '.': case ',': Key=DecimalSeparator;
if (Text.Pos(DecimalSeparator)||(!FEnabfloat)) Key=0; break;
case '-': if (Text.Length()>1) Key=0; break;
case 8: break; //VK_BACK
default: Key=0;
}
TEdit::KeyPress(Key);
}

По заданию, компонент Ed должен обеспечить фильтрацию символов − в


поле редактирования должны отображаться только цифры и допускается в ка-
честве десятичного разделителя либо точка, либо запятая, которая в любом
случае заменяется на символ разделения, установленный в Windows. Для реали-
зации этого в описание класса добавлено объявление функции KeyPress. Эта
функция позволяет отфильтровать только разрешенные символы и объявлена
как DYNAMIC, что и позволяет замещать метод, присущий для базового ком-
понента. После нажатия клавиши клавиатуры вызывается функция
Tedit::KeyPress, которая обеспечивает обработку события KeyPress базовым
компонентом, а затем код клавиши проверяется на допустимость. И если нажа-
та недопустимая клавиша, то код символа не вносится в окно ввода.
После сохранения измененных кодов модулей в созданной для компонента
папке можно протестировать работу нового компонента и пользоваться им.

4.3 Тестирование компонента

После того, как определены поля и функции нового компонента, рекомен-


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

#include <vcl.h>
#pragma hdrstop
#include "Ed.cpp" //подключаем модуль нового компонента
#include "Unit1.h"
//----------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//----------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{ }

TEd *Edi; // объявляем объект созданного класса TEd

//создаем на форме новый компонент Ed


void __fastcall TForm1 ::FormCreate(TObject *Sender)
{ Edi= new TEd(Form1); // выделение динамической памяти
Edi->Parent=Form1;
Edi->Left=Edit1->Left;
Edi->Top=20;
Edi->TabOrder=0;
Button1->Left=Edi->Left+10;
}

//------отклик на кнопку «Скопировать»


void __fastcall TForm1::Button1Click(TObject *Sender)
{ Edit1->Text=Edi->Text; }

Владельцем тестируемого экземпляра (владелец указан как параметр кон-


структора TEd) является окно формы Form1. Свойство Parent созданного экзем-
пляра (его нужно обязательно задать) также равно Form1 – это означает, что но-
вый копнонент Ed будет размещен в окне Form1. В окно Edit1 копируется со-
держимое созданного компонента, который устанавливается при созданни на
формы функцией FormCreate:
104
4.4 Примеры создания и тестирования новых компонентов

Расмотрим еще несколько примеров создания компонентов пользователя


на базе компонентов Edit, StringGrid, Memo и Shape.
Пример 4.3 Создадим компонент типа Edit, в который можно ввести
строку символов либо на латиннице, либо на кириллице, и после нажатия кла-
виши Enter эта введенная строка символов переводится в верхний регистр, а
при повторном нажатии – опять в нижний регистр. Реализация этого примера
приведена ниже. Здесь же приводится приложение, тестирующий этот компо-
нент (в текстах программ полужиром выделены строки, добавленные к стан-
дартному модулю).

Файл uSEd.h− описание класса нового компонента SEd


#ifndef uSEdH
#define uSEdH
//----------------------------------------------------------------
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
//----------------------------------------------------------------
class PACKAGE TSEd : public TEdit
{
private:
bool w;
protected:
public:
__fastcall TSEd(TComponent* Owner);
DYNAMIC void __fastcall KeyDown(Word &Key,
Classes::TShiftState Shift);
__published: };
//----------------------------------------------------------------

#endif

Файл uSEd.cpp. − реализация функций класса нового компонента SEd

#include <vcl.h>
#pragma hdrstop
#include "uSEd.h"
#pragma package(smart_init)
//----------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//
105
static inline void ValidCtrCheck(TSEd *)
{
new TSEd(NULL);
}
//----------------------------------------------------------------
__fastcall TSEd::TSEd(TComponent* Owner)
: TEdit(Owner)
{ w=1; }

void __fastcall TSEd::KeyDown(Word &Key,


Classes::TShiftState Shift)
{ if (Key==13 && w)
{ Text=AnsiUpperCase(Text); w=0;}//false;
else
{Text=AnsiLowerCase(Text); w=1;}//true;
TEdit::KeyDown(Key, Shift);
}
namespace uSEd
{
void __fastcall PACKAGE Register()
{ TComponentClass classes[1] = {__classid(TSEd)};
RegisterComponents("Samples", classes, 0);
}
}

// Тестирование компонента

TSEd *DSEd;

void __fastcall TForm1::Button1Click(TObject *Sender)


{
DSEd= new TSEd(Form1);
DD->Parent=Form1;
DD->Left=Button1->Left-20;
DD->Top=20;
}
Форма проекта с результатами тестирования:
106
Пример 4.4 Создадим компонент на базе StringGrid и на примере этого
компонента рассмотрим различные возможности управления новым элементом:
− при установке курсора мыши в какую-нибудь ячейку и нажатии на кла-
вишу F11, ячейки загрузятся случайными числами;
− при периодическом нажатии курсора мыши в ячейках, будем либо
уменьшать размер цифр в два раза, либо уменьшать их;
− при нажатии клавиши F12 познакомимся с количеством строк и столб-
цов в StringGrid;
− при нажатии клавиши Esc – очистим ячейки.
Заполнить ячейки случайными числами или конкретными значениями
можно также из приложения, осуществляющего тестирование компонента.

Файл SG.h− описание класса нового компонента SG

#ifndef SGH
#define SGH
//----------------------------------------------------------------
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <Grids.hpp>
//----------------------------------------------------------------
class PACKAGE TSG : public TStringGrid
{
private:
bool flag;
AnsiString Name;
protected:
public:
__fastcall TSG(TComponent* Owner);
DYNAMIC void __fastcall KeyDown(Word &Key,
Classes::TShiftState Shift);
DYNAMIC void __fastcall Click(void);
__published:
};
//----------------------------------------------------------------
#endif

Файл SG.cpp − реализация функций класса нового компонента SG

#include <vcl.h>
#pragma hdrstop
#include "SG.h"
#pragma package(smart_init)
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
107
static inline void ValidCtrCheck(TSG *)
{
new TSG(NULL);
}
__fastcall TSG::TSG(TComponent* Owner)
: TStringGrid(Owner)
{ flag=true; Name="Сетка";
}
void __fastcall TSG::KeyDown(Word &Key,
Classes::TShiftState Shift)
{ if (Key==VK_ESCAPE)
for (int i=0; i<RowCount;i++)
for (int j=0; j<ColCount; j++)
Cells[j][i]="";
if (Key==VK_F11)
for (int i=0; i<RowCount;i++)
for (int j=0; j<ColCount; j++)
Cells[j][i]=IntToStr(random(20));
if (Key==VK_F12)
ShowMessage(Name +" "+IntToStr(RowCount)+" "+
IntToStr(ColCount));
TStringGrid::KeyDown(Key, Shift);
}

void __fastcall TSG::Click()


{if (flag) Font->Size=Font->Size*2;
else
Font->Size=Font->Size/2;
flag=!flag;
TStringGrid::Click();
}
namespace Sg
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TSG)};
RegisterComponents("Samples", classes, 0);
}
}

Тестирование компонента

#include "SG.cpp"
TSG *SGt;
void __fastcall TForm1::Button1Click(TObject *Sender)
{
SGt=new TSG(Form1);
SGt->Parent=Form1;
108
SGt->Left=20;
SGt->Top=20;
SGt->FixedRows=0;
SGt->FixedCols=0;
SGt->RowCount=4;
SGt->ColCount=5;
SGt->Options<<goEditing<<goTabs;
for (int j=0;j<SGt->ColCount;j++)
for (int i=0;i<SGt->RowCount;i++)
SGt->Cells[j][i]=IntToStr(random(20)-10);
}

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

а б в
Рис. 4.2. Результаты работи с компонентом SG:
а) при нажатии клавиши <F11> ;
б) при щелчке левой клавишей мыши;
в) при нажатии клавиши <Esc>.

Пример 4.5 Создадим новый компонент на базе компонента Memo. Новый


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

Соответствующие модули и тестирующее приложение приводятся ниже:

Файл Mem.h− описание класса нового компонента Mem


#ifndef MemH
#define MemH
#include <vcl.h>
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
class PACKAGE TMem : public TMemo
109
{
private: int len;
protected:
public:
__fastcall TMem(TComponent* Owner);
DYNAMIC void __fastcall KeyDown(Word &Key,
Classes::TShiftState Shift);
DYNAMIC void __fastcall Change(void);
__published:
};
#endif

Файл Mem .cpp − реализация функций класса нового компонента Mem

рragma hdrstop
#include "Mem.h"
#pragma package(smart_init)

static inline void ValidCtrCheck(TMem *)


{
new TMem(NULL);
}
__fastcall TMem::TMem(TComponent* Owner)
: TMemo(Owner)
{ len=0; }
void __fastcall TMem::KeyDown(Word &Key,
Classes::TShiftState Shift)
{ if (Key==VK_F10)
ShowMessage
("Максимал. строка по длине равна\n "
+ IntToStr(len));
TMemo::KeyDown(Key,Shift);
}
void __fastcall TMem::Change()
{AnsiString L;
int n=Lines->Count;
for (int i=0;i<n;i++)
{ L=Lines->Strings[i];
if (L.Length()>len) len=L.Length();
}
TMemo::Change();
}
namespace Mem
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TMem)};
RegisterComponents("Samples", classes, 0);
}
110
}
// Тестирование компонента

#include "Mem.cpp"

TMem *Mm;

void __fastcall TForm1::Button1Click(TObject *Sender)


{
Mm=new TMem(Form1);
Mm->Parent=Form1;
Mm->Left=20;
Mm->Top=20;
}

Форма проекта и окно сообщений с результатами тестирования:

Пример 4.6 В качестве базового компонента выберем компонент Shape


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

stRectangle – прямоугольник;
stRoundRect – прямоугольник со скругленными углами;
stEllipse – эллипс;
stSquare – квадрат;
stRoundSquare – квадрат со скругленными углами;
stCircle – круг.

Другое существенное свойство компонента – Brush (кисть). Это свойство


является объектом типа TBrush, имеющим ряд подсвойств, в частности: цвет
(Brush->Color) и стиль (Brush->Style) заливки фигуры. Третье свойство ком-
понента Shape – Pen (перо), определяющее стиль линий.
111
Построим наш компонент как наследника класса TShape и назовем наш но-
вый класс Tshap. Этот компонент должен менять свой цвет при нажатии на
кнопке как светофор на перекрестке.

Файл Shap.h − описание класса нового компонента Shap

#ifndef ShapH
#define ShapH
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <ExtCtrls.hpp>
class PACKAGE TShap : public TShape
{private:
bool FOnOff; //разрешает переключение цветов
TColor FOnColor; // цвет первого плана
TColor FOffColor; // цвет второго плана
void __fastcall SetOnOff(const bool Value); //переключение цветов
void __fastcall SetOnColor(const TColor OnColor); //установка цвета
//первого плана
void __fastcall SetOffColor(const TColor OffColor); //установка цвета
//второго плана
protected:
public:
__fastcall TShap(TComponent* Owner);
__published:
__property bool ShapOn={read=FOnOff, write=SetOnOff} ;
};
#endif

Файл Shap .cpp. − реализация функций класса нового компонента Shap

#include <vcl.h>

#pragma hdrstop
#include "Shap.h"
#pragma package(smart_init)
static inline void ValidCtrCheck(TShap *)
{
new TShap(NULL);
}
__fastcall TShap::TShap(TComponent* Owner)
: TShape(Owner)
{ // начальные значения для фигуры
Width=35;
Height=35;
FOnColor=clLime;
112
FOffColor=clRed;
FOnOff=false;
Shape=stEllipse;
Pen->Color=clBlack;
Pen->Width=2;
Brush->Color=FOffColor;
}

void __fastcall TShap::SetOnOff(const bool Value)


{FOnOff=Value;
Brush->Color=(FOnOff)? FOnColor:FOffColor;
}

void __fastcall TShap::SetOnColor(const TColor OnColor)


{FOnColor=OnColor;
Brush->Color=(FOnOff)? FOnColor: FOffColor;
}

void __fastcall TShap::SetOffColor(const TColor OffColor)


{FOffColor=OffColor;
Brush->Color=(FOnOff)? FOnColor: FOffColor;
}
namespace Shap
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TShap)};
RegisterComponents("Samples", classes, 0);
}
}

// Тестирование компонента
#include "Shap.cpp"
//
TShap *Sh;

void __fastcall TForm1::FormCreate(TObject *Sender)


{
Sh=new TShap(this);
Sh->Parent=this;
Sh->Left=60;
Sh->Top=30;
}

void __fastcall TForm1::Button1Click(TObject *Sender)


{ Sh->ShapOn=!Sh->ShapOn; }

Форма проекта тестирования компонента Shap:


113

Пример 4.7 Новый компонент создадим также на базе компонента Shape,


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

Файл Shap2.h − описание класса нового компонента Shap2


#ifndef Shap2H
#define Shap2H
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <ExtCtrls.hpp>
class PACKAGE TShap2: public TShape
{
private:
int FSkor, FXc,FYc; //координаты исходной точки и скорость перемещения
protected:
void __fastcall SetSkor(int sk);
public:
__fastcall TShap2(TComponent* Owner);
bool __fastcall Dvigen(); // функция для перемещения компонента
__published:
__property int Skor={read=FSkor, write=SetSkor};
__property int Xc={read=FXc, write=FXc};
__property int Yc={read=FYc, write=FYc};
};
#endif

Файл Shap2.cpp. − реализация функций класса нового компонента Shap2


#include <vcl.h>
#pragma hdrstop
#include "Math.h"
#include "Shap2.h"
#pragma package(smart_init)
114
static inline void ValidCtrCheck(TShap *)
{
new TShap2(NULL);
}
__fastcall TShap::TShap2(TComponent* Owner)
: TShape(Owner)
{ FSkor=3; FXc=220; FYc=220;
Shape=stCircle;
Brush->Color=clBlue;
Pen->Color=clBlack;
Pen->Width=2;
}
void __fastcall TShap2::SetSkor(int sk)
{ if ((sk>0)&&(sk<25)) FSkor=sk; else FSkor=3; }
bool __fastcall TShap2::Dvigen()
{int X=Left+Width/2;
int Y=Top+Height/2;
float L=sqrt((Xc-X)*(Xc-X)+(Y-Yc)*(Y-Yc));
if (L>1)
{float c=(Xc-X)/L;
float s=(Y-Yc)/L;
Left+=Skor*c;
Top-=Skor*s;
return true;
} else return false;
}
namespace Shap2
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TShap2)};
RegisterComponents("Samples", classes, 0);
}
}

Тестирование компонента

На тестирующем приложении устанавливается Tymer и компонент Shape в


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

#include "Shap2.cpp"

TShap2 *Sha;
int tt=0;

void __fastcall TForm1::FormCreate(TObject *Sender)


{
115
Sha=new TShap(Form1);
Sha->Parent=Form1;
Sha-> Width=30;
Sha->Height=30;
Shape1->Left=Sha->Xc;
Shape1->Top=Sha->Yc;
}

void __fastcall TForm1::Timer1Timer(TObject *Sender)


{
if(!(Sha->Dvigen())){Timer1->Enabled=false; tt=-5;
Button1->Caption="Продолжить";}
Sha->Left=Sha->Left+tt;
Sha->Top=Sha->Top+tt;
if (Sha->Left<0)
{Timer1->Enabled=false; tt=0;
Button1->Caption="Начали";}
}

void __fastcall TForm1::Button1Click(TObject *Sender)


{ Timer1->Enabled=true; }

Форма проекта тестирования компонента Shap2:

Контрольные вопросы

1 Какие элементы являются основными в классе компонентов?


2 Какой класс является базовым для всех визуальных компонентов?
3 Какой класс является базовым для всех оконных компонентов?
4 Какой класс является базовым для графических компонентов?
5 Назовите методы класса TControl и их назначение.
6 Назовите методы класса TWinControl и их назначение.
7 Назовите последовательность процесса создания модуля нового компо-
нента.
8 Для чего нужна установка нового компонента?
116
КОМПЛЕКСНОЕ ЗАДАНИЕ
на тему: «Создание компонента пользователя»

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


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

Варианты зaдaний для выполнения нa компьютеpе


1. Построить класс − потомок класса TLabel, объекты которого при щелч-
ке правой кнопкой мыши увеличивают размер букв своего Caption вдвое, а при
повторном щелчке правой кнопкой восстанавливают предыдущие размеры
букв.
2. Построить класс − потомок класса TStringGrid, объект которого при на-
жатии клавиши F11 выводит в отдельном окне сообщения о своих параметрах:
Name, координаты размещения, число строк и колонок.
3. Построить класс − потомок класса TMemo, объекты которого при нажа-
тии клавиши F11 увеличивают свой размер на 50 пикселей в высоту и на 75 в
ширину, а при нажатии F12 восстанавливают предыдущие размеры.
4. Построить класс − потомок класса TLabel, объекты которого при щелч-
ке правой кнопкой мыши выполняют "обмен местами" пары цветов [цвет фона
− цвет букв Caption], а при повторном щелчке правой кнопкой восстанавливают
предыдущие цвета.
5. Построить класс − потомок класса TEdit, объект которого при нажатии
клавиши ENTER перемещается в верхний левый угол формы, а при повторном
нажатии возвращается на старое место.
6. Построить рейтинг − потомок класса TButton, объект которого при на-
давливании (щелчке левой кнопкой мыши) выдает столько звуковых сигналов,
сколько всего было нажатий данного объекта.
7. Построить класс − потомок класса TMemo, объект которого при нажа-
тии клавиши F11 выводит сообщение о том, вводился ли когда-нибудь в его ок-
но некий текст, содержавший слово ОБЪЕКТ.
8.Построить класс − потомок класса TStringGrid, объект которого при на-
жатии клавиши F11 выдает столько звуковых сигналов, сколько заполненных
ячеек (Cells) в окне данного объекта.
9. Построить класс − потомок класса TEdit, объект которого при нажатии
клавиши ENTER выводит сообщение о длине максимально длинного текста,
который когда−либо находился в его окне.
10. Построить класс − потомок класса TMemo, у объектов которого при
нажатии клавиши F11 появляются "бегунки" (ScrollBars), а при повторном на-
жатии F11 "бегунки" исчезают.
11. Построить класс − потомок класса TButton, объекты которого при на-
жатии (щелчке левой кнопкой мыши) увеличивают свой размер на 15 пикселей
117
в высоту и на 20 в ширину, а при повторном нажатии восстанавливают преды-
дущие размеры.
12.Построить класс − потомок класса TLabel, объект которого при щелчке
правой кнопкой мыши выводит в отдельном окне сообщения о своих парамет-
рах: Name, Caption, координаты размещения и число символов в Caption.
13. Построить класс − потомок класса TMemo, у объектов которого, по-
мимо стандартного окна для ввода текста, присутствует еще и окно (или над-
пись), в котором постоянно отображается число символов.
14. Построить класс − потомок класса TButton, объекты которого при на-
жатии (щелчке левой кнопкой мыши) исчезают на 1 секунду, а потом снова по-
являются.
15. Построить класс − потомок класса TEdit, объект которого при нажатии
клавиши ENTER меняет все буквы своего Text на большие; а при повторном
нажатии восстанавливает прежний размер букв.
16. Построить класс − потомок класса TStringGrid, у объектов которого
при нажатии клавиши F11 исчезают "бегунки" (ScrollBars), а при повторном
нажатии F11 "бегунки" появляются.
17. Построить класс − потомок класса TMemo, объект которого при нажа-
тии клавиши F11 выдает столько звуковых сигналов, сколько всего есть строк в
окне данного объекта.
18. Построить класс − потомок класса TLabel, объекты которого при щелч-
ке правой кнопкой мыши исчезают на 1.5 секунды, а потом снова появляются.
19. Построить класс − потомок класса TStringGrid, у объектов которого
при нажатии клавиши F11 исчезают фиксированные строки и колонка
(FixedRow и FixedCol), а при повторном нажатии F11 они снова появляются.
20. Построить класс − потомок класса TButton, объект которого при нажа-
тии (щелчке левой кнопкой мыши) выводит в отдельном окне сообщения о сво-
их параметрах: Name, Caption, координаты размещения и число нажатий.
21. Построить класс − потомок класса TEdit, объекты которого при нажа-
тии клавиши ENTER выполняют "обмен местами" пары цветов (цвет фона −
цвет букв), а при повторном нажатии восстанавливают предыдущие цвета.
22. Построить класс − потомок класса TMemo, объект которого при нажа-
тии клавиши F11 выводит сообщение о длине максимально длинного текста,
который когда−либо находился в его окне.
23. Построить класс − потомок класса TStringGrid, у объектов которого
при нажатии клавиши F11 все пустые ячейки Cells заполняются строкой из трех
символов # # #, а при повторном нажатии F11 эти ячейки снова очищаются.
24. Построить класс − потомок класса TLabel, объект которого при щелчке
правой кнопкой мыши меняет все буквы своего Caption на большие, а при по-
вторном щелчке правой кнопкой восстанавливает прежний размер букв.
25. Построить класс − потомок класса TEdit, объект которого при нажатии
клавиши ENTER выводит сообщение о том, вводилась ли когда-либо какая тек-
стовая информация в его окно.
26. Построить класс − потомок класса TStringGrid, у объектов которого
при нажатии клавиши ESC все ячейки Cells очищаются.
118
27. Построить класс − потомок класса TStringGrid, объект которого при
нажатии клавиши F11 транспонирует матрицу в своем окне (меняет местами
строки с колонками в Cells).
28. Построить класс − потомок класса TButton, объект которого при нажа-
тии (щелчке левой кнопкой мыши) перемещает компонент вправо на 20 пиксе-
лей и в отдельном окне сообщения − число нажатий.
29. Построить класс − потомок класса TEdit, объекты которого при нажа-
тии клавиши F12 выполняют "обмен местами" пары цветов (цвет фона − цвет
букв), а при повторном нажатии восстанавливают предыдущие цвета.
30. Построить класс − потомок класса TMemo, объект которого при нажа-
тии клавиши F11 выводит сообщение о количестве введенных строк и меняет
текст символов на красный.

Литература

1 Информатика. Модуль 4. Программирование задач со списками и файла-


ми. Объектно-ориентированное программирование / [Швайко И.Г., Про-
коп Ю.В., Леоненко Л.Л., Северин М.В.] − Одеса: ОНАЗ, 2008. – 104 с.
2 С++. Основи програмування. Теорія та практика: [підручник] / [О.Г. Тро-
фименко, Ю.В. Прокоп, І.Г. Швайко та ін. ] − Одесса: Фенікс, 2010. –
544 с.
3 Седжвик Р. Фундаментальные алгоритмы на С++ / Седжвик Р − М. Диа-
Софт.,2001. – 688 с.
4 Топп У. Структуры данных в С++ / У. Топп, У. Форд – М.: Бином, 2000. –
816 с.
5 Вирт Н. Алгоритмы и структуры данных / Вирт Н. – М.: Мир, 1989. –
896 с.
6 Архангельский А.Я. Программирование в С++ Builder 6 и 2006 / А.Я. Ар-
хангельский, М.А. Тагин − М.: Бином-Пресс, 2007. – 1184 с.
7 Архангельский А.Я. Приемы программирования в С++ Builder 6 и 2006:
вычислительные задачи, работа с документами / А.Я. Архангельский,
М.А. Тагин. − М.: Бином-Пресс, 2008. – 1238 с.
8 Вирт Н. Систематическое программирование / Вирт Н. − М. Мир., 1976. –
182 с.
9 Шамис В.А. С++ Builder 5. Техника визуального программирования / Ша-
мис В.А. – М.: Нолидж, 2001. – 688 с.
10 Лафоре Р. Объектно-ориентированное программирование в С++ / Лафоре
Р – СПб.: Питер, 2003. – 928 с.
11 Павловская Т.А. С++ Программирование на языке высокого уровня / Пав-
ловская Т.А. – СПб.: Питер, 2003. – 464 с.
12 Галисеев Г.В. Программирование на языке С#. Самоучитель / Галисе-
ев Г.В. – М.: Изд. дом “Вильямс”, 2006. – 368 с.
119
Содержание

Предисловие………………………………………………………………..…..3

1 ЭЛЕМЕНТЫ ОБЪЕКТНО-ОРИЕНТИРОВАННОГО
ПРОГРАММИРОВАНИЯ……………………………………………………5
Лабораторная работа № 2.1 Программирование создания
классов и их объектов….…...……………………………………….……..37

2 НАСЛЕДОВАНИЕ КЛАССОВ ……………………………………..……...45


Лабораторная работа № 2.2 Программирование классов
с иерархической структурой …….…………………………………..…….61

3 ПОЛИМОРФИЗМ …………………….…………………………..…………71
Лабораторная работа № 2.3 Программная реализация свойства
полиморфизм ….........................................................................................…84

4 КЛАССЫ И ОБЪЕКТЫ БИБЛИОТЕКИ ВИЗУАЛЬНЫХ


КОМПОНЕНТ ………………………………………………………………94

Комплексное задание ………………………………………………… ….....116

Литература ....………… ……………………………………………….……..118


120

Навчальне видання

Шаповаленко Валентина Андріївна


Швайко Ігор Григорович

Технології програмування

МОДУЛЬ № 1

Частина 2

Об’єктно-орієнтоване програмування

Методичні вказівки
до лабораторних й практичних занять

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


“Телекомунікації”,
“Автоматизація і комп’ютерно-інтегровані технології”

Редактор Гусак В.Т.


Верстка Гардиман Ж.А.

Здано в набір 15.03.2011 Підписано до друку 20.05.2011


Формат 60x90/16 Зам. № 4527
Наклад 500 прим. Обсяг 7,25 друк. арк.
Віддруковано на видавничому устаткуванні фірми RISO
у друкарні редакційно-видавничого центру ОНАЗ ім. О.С. Попова
м. Одеса, вул. Ковалевського, 5
Тел. 720-78-94
ОНАЗ, 2011

Вам также может понравиться