![](/user_photo/2706_HbeT2.jpg)
- •Предисловие
- •Глава 1. Основные понятия
- •1.1. Элементы языка программирования
- •1.2. Процесс создания программы
- •1.3. Первая программа
- •1.4. Состав программы
- •Глава 2. Средства разработки на C++
- •2.1. Системы Turbo C++ 3.0/Borland C++ 3.1
- •2.2. Система C++ Builder
- •Глава 3. Работа с числовыми данными
- •3.1. Целые типы
- •3.2. Числа с плавающей точкой
- •3.3. Ввод и вывод чисел
- •3.4. Логический тип и логические операции
- •3.5. Математические функции
- •Глава 4. Операторы. Ключевые слова
- •4.1. Операторы
- •4.2. Приоритеты операторов
- •4.3. Ключевые слова
- •4.4. Структура программы
- •4.5. Константы
- •Задачи - . Простейшие вычисления
- •Глава 5. Управление и циклы
- •5.1. Условный оператор
- •5.2. Операторы цикла
- •5.3. Переключатель
- •5.4. Операторы break и continue
- •Задачи -. Выбор и циклы
- •Глава 6. Массивы
- •6.1. Одномерные массивы
- •6.2. Двумерные массивы
- •Задачи -. Одно- и двумерные массивы
- •Глава 7. Функции
- •7.1. Определение функции
- •7.2. Формальные параметры и фактические аргументы
- •7.3. Автоматические и статические переменные
- •7.4. Прототипы функций
- •7.5. Массивы как аргументы функций
- •7.6. Внешние переменные
- •7.7. Рекурсия
- •7.8. Перегруженные имена функций
- •7.9. Аргументы функций по умолчанию
- •Задачи -. Функции
- •Глава 8. Символы и строки
- •8.1. Символы
- •8.2. Строки символов
- •Задачи -. Символы и строки
- •Глава 9. Препроцессор
- •9.1. Директивы препроцессора
- •9.2. Макросы
- •Задачи -. Макросы
- •Глава 10. Указатели и ссылки
- •10.1. Указатели и адреса
- •10.2. Указатели и массивы
- •10.3. Адресная арифметика
- •10.4. Символьные указатели
- •10.5. Массивы указателей
- •10.6. Указатели на функции
- •10.7. Ссылки
- •10.8. Операторы new и delete
- •Задачи -. Указатели и ссылки
- •Глава 11. О файлах и командной строке
- •11.1. Знакомство с файлами
- •11.2. Командная строка
- •11.3. Перенаправление стандартного ввода и вывода на файл
- •11.4. Аргументы командной строки
- •Задачи -. Файлы и командная строка
- •Глава 12. Работа с экраном дисплея
- •12.1. Текстовый режим
- •12.2. Графический режим
- •Задачи -. Работа с экраном
- •Глава 13. Внутреннее представление чисел
- •13.1. Двоичная система счисления
- •13.2. Беззнаковые целые
- •13.3. Двоичный дополнительный код
- •13.4. Двоичный код с избытком
- •13.5. Побитовые операторы
- •13.6. Дробные числа в двоичной системе
- •13.7. Внутреннее представление плавающих типов
- •13.8. Преобразование типов
- •Задачи -. Побитовые операторы
- •Глава 14. Структуры, перечисления, объединения
- •14.1. Объявление структур
- •14.2. Структуры и функции
- •14.3. Указатели на структуры
- •14.4. Массивы структур
- •14.5. Перечисления
- •14.6. Объединения
- •14.7. Битовые поля
- •14.8. О бинарных файлах
- •Задачи -. Структуры
- •Глава 15. Классы
- •15.1. Структуры в C++. Инкапсуляция
- •15.2. Встроенные функции
- •15.3. Классы. Скрытие данных
- •15.4. Конструкторы
- •15.5. Статические члены класса
- •15.6. Друзья класса
- •15.7. Копирование объектов класса
- •15.8. Управление доступом
- •15.9. Ссылка на себя
- •15.10. Деструкторы
- •Задачи -. Работа с классами
- •Глава 16. Программы из нескольких файлов
- •16.1. Работа с проектами
- •16.2. Область действия имен
- •16.3. Заголовочные файлы
- •16.4. Пространства имен
- •Задачи -. Работа со стеком
- •Глава 17. Перегрузка операторов
- •17.1. Правила перегрузки операторов
- •Задачи -. Перегрузка операторов
- •Глава 18. Конструктор копирования и оператор присваивания
- •18.1. Проблемы при копировании
- •Задачи -. Конструктор копирования
- •Глава 19. Ввод и вывод
- •19.1. Вывод
- •19.2. Ввод
- •19.3. Ввод и вывод определяемых пользователем типов
- •19.4. Работа с файлами
- •Глава 20. Взаимоотношения классов
- •20.1. Объекты как члены класса
- •20.2. Конструкторы встроенных типов
- •20.3. Наследование
- •20.4. Виртуальные функции
- •20.5. Абстрактные классы
- •20.6. Совместимость типов
- •20.7. Множественное наследование
- •Задачи -. Наследование классов
- •Глава 21. Шаблоны, исключения
- •21.1. Шаблоны
- •21.2. Шаблоны функций
- •21.3. Классы и шаблоны
- •21.4. Обработка исключений
- •21.5. Стандартная библиотека шаблонов
- •Литература
- •Предметный указатель
272
Глава 20. Взаимоотношения классов
На базе ранее разработанных классов можно создавать новые классы. Это можно делать, включая объекты одного класса в состав другого, например, так как прямоугольник rect включает две точки point в программе 36, или делая новый класс производным от другого.
20.1. Объекты как члены класса
Пусть есть класс:
class Member { int a;
public:
Member (int i) // Конструктор класса Member {a = i;}
};
Объявим новый класс, который будет включать в качестве своего члена объект класса Member:
сlass Container{ |
// Класс, содержащий объект другого класса |
|
Member aa; |
// Объект aa класса Member – член класса Container |
|
double x; |
|
|
public: |
|
|
Container(int i, double xx); |
// Конструктор |
|
}; |
|
|
При создании объекта класса Container должен создаваться и объект aa класса Member, для чего должен вызываться соответствующий конструктор. Это реализуется с помощью специальной синтаксической конструкции:
Container :: Container(int i, double xx): aa(i) //Конструктор Container { x = xx; }
После заголовка конструктора ставится двоеточие, за которым идет имя объекта и в скобках аргументы конструктора объекта. Далее идет реализация остальной части конструктора класса, содержащего объект.
![](/html/2706/175/html_zl27x8vZdH.bmrn/htmlconvd-ehB0QZ273x1.jpg)
Взаимоотношения классов 273
20.2. Конструкторы встроенных типов
Встроенные типы можно рассматривать как классы, имеющие конструкторы, поэтому конструктор для Member можно реализовать в виде:
Member(int i): a(i) {}
Здесь запись a(i) означает вызов конструктора встроенного типа int. Аналогично, конструктор класса Container можно записать в виде:
Container :: Container(int i, double xx): aa(i), x(xx) { }
Запись x(xx) означает вызов конструктора для типа double.
Программа 58. Личные данные
Напишем программу, для работы с массивами персональных сведений о людях. Эти сведения должны включать фамилию человека и его дату рождения.
Создадим модуль DateCls для класса моделирования календарных дат, внеся в этот класс конструктор копирования и оператор сравнения дат.
// Файл DateCls.H
#ifndef DateClsH #define DateClsH
class Date
{
int d, m, y;
static int d0, m0, y0; static int dw0;
public:
Date(int = 0, int = 0, int = 0); Date(Date&);
//Класс для работы с датами
//День, месяц, год
//Начальная дата
//День недели начальной даты.
//Конструктор
//Конструктор копирования
static void SetInitDate(int di, int mi, int yi, int dwi) { d0 = di; m0 = mi; y0 = yi; dw0 = dwi; }
int DayOfYear(); |
// Номер дня в году |
long Diapason(Date dt); |
// Диапазон между двумя датами |
void PrintDate(); |
// Печать даты |
char* WeekDay(); |
// Название дня недели |
bool operator<=(Date&); |
// Сравнение двух дат |
}; |
|
bool Leap(int year); |
// Проверка високосный или нет год year |
#endif |
|
![](/html/2706/175/html_zl27x8vZdH.bmrn/htmlconvd-ehB0QZ274x1.jpg)
274 20
Реализацию класса Date поместим в файле DateCls.CPP. Ниже приводится только та часть этого файла, которая отличается от рассмотренных ранее реализаций класса дат (см. программы 42-48).
// Файл DateCls.cpp #include ”DateCls.h”
Date :: Date(Date& D) |
// Конструктор копирования |
{ d = D.d; m = D.m; y = D.y; } |
// создает копию даты D |
bool Date :: operator<=(Date& D) |
// Сравнение двух дат |
{ |
|
return (y < D.y) || (y == D.y && m < D.m)
|| (y == D.y && m == D.m && d <= D.d);
}
Создадим модуль Persons, в котором разместим классы для работы со сведениями об отдельной личности и о группе лиц. Далее приводится заголовочный файл этого модуля:
// Файл Persons.h |
|
#ifndef PersonsH |
|
#define PersonsH |
|
#include "DateCls.h" |
// Подключение класса Date |
#include <iostream.h> |
|
#include <fstream.h> |
|
#include <string.h> |
|
class Pers |
// Класс со сведениями о человеке |
{ |
|
char* name; |
// Строка с фамилией и инициалами |
Date bd; |
// Дата рождения |
public: |
|
Pers(char*, Date); |
// Конструктор |
Pers(); |
// Конструктор по умолчанию |
Pers(Pers&); |
// Конструктор копирования |
~Pers() |
// Деструктор |
{ delete[] name;} |
// Удаление строки с именем |
Pers& operator = (Pers &); |
// Оператор присваивания |
void Print(); |
// Вывод сведений о человеке |
//CmpDate: возвращает true, если дата объекта ps1
//более ранняя или та же, что и у объекта ps2
friend bool CmpDate(Pers& ps1, Pers& ps2) |
// Используется |
|
{ return ps1.bd <= ps2.bd; } |
// функция-оператор сравнения дат |
};
|
Взаимоотношения классов 275 |
class Persons |
// Класс для моделирования группы лиц |
{ |
|
Pers* G; |
// Массив сведений о людях |
int size; |
// Размер массива |
int n; |
// Текущее число людей в группе |
public: |
|
Persons(int size = 25); |
// Конструктор. По умолчанию 25 человек |
void Add(Pers&); |
// Добавление человека в группу |
void Print(); |
// Печать группы |
~Persons(); |
// Деструктор |
friend void ReadFromFile(ifstream& inf, Persons&); // Чтение данных
// о группе из файла void SortPersons(bool (*Compare)(Pers& ps1, Pers& ps2)); // Сортировка // массива по критерию, задаваемому функцией сравнения Compare
void Swap(int i, int j); // Поменять местами элементы i и j массива G
};
#endif
Теперь приведем файл реализации модуля Persons.cpp.
// Файл Persons.cpp #include "Persons.h"
Конструктор класса Pers пишем по изложенному выше правилу:
Pers :: Pers(char *s, Date D): bd(D) |
// Конструктор |
|
{ |
// Сначала вызывается конструктор для объекта bd |
|
|
name = new char [strlen(s)+1]; |
// Выделение памяти под имя |
} |
strcpy(name, s); |
// Копирование имени |
|
|
Так как у класса Date есть конструктор по умолчанию, он будет вызываться в следующем конструкторе по умолчанию для класса Pers:
Pers :: Pers() |
// Конструктор по умолчанию |
|
{ |
// Текущая дата как дата рождения |
|
name = NULL; |
// Пустое имя |
|
} |
|
|
Pers::Pers(Pers& ps) |
|
// Конструктор копирования |
{ |
|
|
name = new char [strlen(ps.name) + 1]; |
// Выделение памяти под имя |
|
strcpy(name, ps.name); |
|
// Копирование имени |
bd = ps.bd; |
|
// Копирование даты |
} |
|
|
Pers& Pers :: operator = (Pers& ps) // Перегрузка оператора присваивания
276 |
20 |
|
{ |
|
|
if (this != &ps){ |
// Если присваивание не самому себе, |
|
delete[] name; |
// удаление старого имени, |
|
name = new char [strlen(ps.name) + 1]; // память под новое имя, |
||
strcpy(name, ps.name); |
// копирование имени, |
|
bd = ps.bd; |
// копирование даты |
|
} |
|
|
return *this; |
// Возвращение ссылки на объект |
|
} |
|
|
void Pers :: Print() |
// Вывод сведений о человеке |
|
{ |
|
|
cout << name << " \t"; |
// Вывод имени |
|
bd.PrintDate(); |
// Вывод даты |
|
} |
|
|
Здесь после вывода имени печатается несколько пробелов и символ табуляции, чтобы даты выравнивались по вертикали. Число пробелов определяется подбором.
Далее идет реализация класса Persons, моделирующего группу.
Persons :: Persons(int sz) |
// Конструктор |
{ |
|
G = new Pers[size = sz]; |
// Выделение памяти под массив |
n = 0; |
// Вначале группа пуста |
} |
|
Создание массива объектов класса Pers возможно потому, что в этом классе есть конструктор по умолчанию, который создает объекты с пустыми фамилиями и текущими датами.
Без наличия в классе конструктора по умолчанию создание массива объектов класса невозможно.
Persons :: ~Persons() |
// Деструктор |
{ delete[] G; } |
// Освобождение памяти |
При удалении каждого объекта из массива для него будет автоматически вызван деструктор, так как при создании каждого объекта использовался конструктор.
void Persons :: Add(Pers& st) |
// Добавление в группу |
{ |
// нового человека |
if (n == size){ |
// Если в группе нет |
cout << "Мест нет\n"; |
// мест, то выход |
return; |
|
} |
|
G[n++] = st; |
// Добавление сведений о новом члене группы |
} |
|
![](/html/2706/175/html_zl27x8vZdH.bmrn/htmlconvd-ehB0QZ277x1.jpg)
Взаимоотношения классов 277
В инструкции G[n++] = st; используется перегруженный оператор присваивания класса Pers.
void Persons :: Print() |
// Печать группы |
{ |
|
for(int i = 0; i < n; i++) |
// Печать данных |
G[i].Print(); |
// о каждом человеке |
} |
|
void ReadFromFile(ifstream& inf, Persons& grp) // Чтение данных |
|
{ |
// о группе из файла |
const int N = 200; |
|
int ng; |
// Число людей в группе |
char s[N]; |
// Массив для фамилии |
int d, m, y; |
|
inf >> ng; |
// Чтение размера группы |
inf.getline(s, N); // Чтение '\n' из первой строки и переход к следующей for(int i = 0; i < ng; i++){
inf.getline(s, N); // Чтение из файла строки с фамилией и инициалами
inf >> d >> m >> y; |
//Чтение компонентов даты |
|
grp.Add(Pers(s, Date(d, m, y))); |
// Добавление сведений в группу |
|
inf.getline(s, N); |
// Чтение '\n' для перехода на следующую строку |
}
}
//Сортировка массива методом пузырька по критерию,
//задаваемому функцией сравнения Compare
void Persons :: SortPersons(bool (*Compare)(Pers& ps1, Pers& ps2))
{
for(int i = n - 1; i > 0; i--) |
|
for(int j = 0; j < i; j++) |
|
if(!Compare(G[j], G[j + 1])) |
//Если не тот порядок элементов, |
Swap(j, j + 1); |
//переставить их |
} |
|
Аргументом функции сортировки является указатель Compare на функцию, которая сравнивает личные сведения двух людей.
void Persons :: Swap(int i, int j) //Переставить элементы i и j массива G
{
Pers tmp = G[i]; G[i] = G[j]; G[j] = tmp;
}
В главной программе создадим один объект класса Persons (одну группу). Заполним объект сведениями, прочитанными из файла
![](/html/2706/175/html_zl27x8vZdH.bmrn/htmlconvd-ehB0QZ278x1.jpg)
278 20
PersData.txt, распечатаем, отсортируем по возрастанию даты рождения и снова распечатаем. Все это – в файле MainPers.cpp:
// Файл MainPers.cpp |
|
#include "Persons.h" |
|
int main() |
|
{ |
|
Persons Writers; |
// Writers – объект класса Persons |
ifstream inf("PersData.txt"); |
// Создаем файловый поток для чтения |
if(!inf){ |
// Если файл не удалось открыть, |
cerr << "Не удалось открыть файл PersData.txt "; |
|
exit(1); |
// завершаем программу |
} |
|
ReadFromFile(inf, Writers); |
// Чтение сведений из файла |
cout << "Состав группы: \n"; |
|
Writers.Print(); |
// Вывод состава группы |
// Сортировка по возрастанию даты рождения |
|
Writers.SortPersons(CmpDate); |
|
cout << "\nСостав группы по возрастанию даты: \n"; |
|
Writers.Print(); |
// Вывод состава группы |
cin.get(); |
// Ждем нажатия Enter |
return 0; |
|
} |
|
Текстовый файл исходных данных можно создать любым редактором, в частности редактором среды разработки, с помощью которого создаются исходные тексты программ. Подготовим в файле PersData.txt следующие исходные данные:
9 Толстой Л.Н. 9 9 1828
Пушкин А.С. 6 6 1799
Лермонтов М.Ю. 15 10 1814 Крылов И.А.
13 2 1769 Тургенев И.С. 28 10 1818 Ломоносов М.В. 19 11 1711 Горький А.М. 28 3 1868
Достоевский Ф.М. 11 11 1821 Шолохов М.А.