- •Предисловие
- •Глава 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; }
После заголовка конструктора ставится двоеточие, за которым идет имя объекта и в скобках аргументы конструктора объекта. Далее идет реализация остальной части конструктора класса, содержащего объект.
Взаимоотношения классов 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 |
|
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; |
// Добавление сведений о новом члене группы |
} |
|
Взаимоотношения классов 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 (одну группу). Заполним объект сведениями, прочитанными из файла
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 Шолохов М.А.