Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Tekhnologia_programmirovania.pdf
Скачиваний:
182
Добавлен:
08.04.2015
Размер:
1.76 Mб
Скачать

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 Шолохов М.А.