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

Классы 215

15.9. Ссылка на себя

Каждая нестатическая функция-член «знает», для какого объекта она вызвана и может явно на него ссылаться, использую ключевое слово this, являющееся указателем на объект, для которого вызвана функция.

Программа 47. Модификация дат

В данной программе написаны функции для модификации даты, которые возвращают ссылку на измененную дату.

// Файл DateCl_6.cpp

 

class Date{

 

int d, m, y;

// День, месяц и год

public:

 

Date(int = 0, int = 0, int = 0);

// Конструктор

Date& Add_Year(int n);

// Добавить к дате n лет

Date& Add_OneDay();

// Добавить к дате 1 день

Date& Add_Day(int n);

// Добавить к дате n дней

Date& Add_OneMonth();

// Добавить к дате 1 месяц

Date& Add_Month(int n);

// Добавить к дате n месяцев

void Print();

 

};

 

#include <iostream.h>

 

void Date::Print()

 

{

 

cout << d << '.' << m <<'.'<< y;

 

}

 

// leap: возвращает 1, если год year високосный и 0, если нет int leap(int year)

{ return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; }

Date& Date::Add_Year(int n)

{

if(m == 2 && d == 29 && leap(y + n) != 1){ m = 3; d = 1; }

y += n;

 

return *this;

// Возвращаем ссылку на измененный объект

}

 

Здесь внутри функции Add_Year ключевое слово this имеет значение указателя на тот объект класса Date, для которого функция вызвана. Например, далее в main есть инструкция вызова функции Add_Year для объекта Studies:

Studies.Add_Year(4);

216 15

Здесь во время работы Add_Year указатель this имеет значение адреса переменной Studies, а выражение *this есть сам объект Studies.

// Массив дней в месяцах для невисокосного и високосного года char daytab[2][13] ={{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},

{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} };

Date& Date::Add_OneDay()

 

{

 

 

if(d < daytab[leap(y)][m])

// Не последний день месяца

d++;

 

 

else if(m < 12){

// Последний день месяца, но не последний года

d = 1; m++;

 

 

}

 

 

else{

 

// Последний день года

d = 1; m = 1; y++;

 

 

}

return *this;

}

Увеличение даты на n дней реализуется n-кратным вызовом функции увеличения даты на 1 день.

Date& Date::Add_Day(int n)

// Добавить к дате n дней

{

 

for(int i = 0; i < n; i++)

// Вызываем n раз функцию

Add_OneDay();

// увеличения даты на 1 день

return *this;

 

}

 

Если исходная дата приходится на декабрь, через месяц будет то же число января следующего года. Для 28, 29, 30 или 31 января, считаем, что через 1 месяц будет последнее число февраля. Последнее число следующего месяца будет также, если исходная дата – последнее число месяца, независимо от числа дней в месяцах.

Date& Date::Add_OneMonth()

// Добавить к дате 1 месяц

{

 

if(m == 12){

// Если декабрь,то

m = 1; y++;

// будет январь следующего года

}

else if((m == 1 && d > 28) || d == 31){ // Если январь, число > 28 или

 

// последний день промежуточного месяца,

m++;

// наступит последнее число

d = daytab[leap(y)][m];

// следующего месяца

}

 

else

 

 

Классы

217

m++;

// Следующий месяца

 

return *this;

 

 

}

 

 

Date& Date::Add_Month(int n)

// Добавить к дате n месяцев

 

{

 

 

for(int i = 0; i < n; i++)

// n раз

 

Add_OneMonth();

// добавляем по 1 месяцу

 

return *this;

 

 

}

 

 

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

 

 

#include <dos.h>

 

 

Date::Date(int dd, int mm, int yy)

 

 

{

 

 

date sysd;

// Системная дата

 

getdate(&sysd);

// Получение системной даты

 

d = dd ? dd : sysd.da_day;

 

 

m = mm ? mm : sysd.da_mon;

 

 

y = yy ? yy : sysd.da_year;

 

 

}

 

 

#include <conio.h> int main()

{

Date Studies(1, 9, 2005);

cout << "Начало учебы: "; Studies.Print(); cout << endl; Studies.Add_Year(4);

Studies.Add_Month(9);

Studies.Add_Day(21);

cout << "Конец учебы: "; Studies.Print(); cout << endl;

Date Today; // Использование конструктора по умолчанию, сегодня cout << "Сегодня: "; Today.Print(); cout << endl; Today.Add_Year(1).Add_Month(1).Add_Day(1);

cout << "Через 1 год, 1 месяц и 1 день будет "; Today.Print(); cout << endl;

getch(); return 0;

}

Программа выдает:

Начало учебы: 1.9.2005 Конец учебы: 22.6.2010 Сегодня : 12.4.2006

Через 1 год 1 месяц 1 и один день будет 13.5.2007

Обсудим инструкцию

218 15

Today.Add_Year(1).Add_Month(1).Add_Day(1);

Когда есть несколько операторов точка (.), они выполняются слева направо, то есть приведенная инструкция эквивалентна следующей:

((Today.Add_Year(1)).Add_Month(1)).Add_Day(1);

Выражение Today.Add_Year(1) во внутренних скобках равно дате Today, увеличенной на 1 год, так как Add_Year возвращает ссылку на объект, для которого вызывается. Далее для этой измененной Today вызывается Add_Month, которая также возвращает дату Today, увеличенную на 1 месяц, которая затем увеличивается на 1 день функцией Add_Day. Возможность писать подобные цепочки вызовов функций обеспечивается тем, что функции возвращают ссылки на объекты.

15.10.Деструкторы

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

т.п. При уничтожении объектов ресурсы должны освобождаться. Эту работу выполняют специальные функции-члены класса – деструкторы.

Имя деструктора совпадает с именем класса, только впереди добавляется знак ~ (тильда). Деструктор не имеет аргументов, и так же, как и конструктор, ничего не возвращает.

Деструкторы вызываются неявно, когда автоматическая переменная выходит из зоны видимости, когда оператором delete удаляется объект, созданный в свободной памяти оператором new.

Программа 48. Деструктор в классе дат

В классе дат деструктор не нужен, так как при создании переменных типа Date никакие ресурсы не выделяются, кроме памяти под d, m, y. При уничтении переменной типа Date, занимаемая ею память освобождается встроенными средствами. Можно сказать, что при этом работает деструктор по умолчанию.

Включим все-таки в класс Date деструктор, который будет лишь сигнализировать о своей работе.

// Файл DateCl_7.cpp

 

class Date{

 

int d, m, y;

// День, месяц и год

// …

 

public:

 

Date(int = 0, int = 0, int = 0);

// Конструктор

~Date();

// Деструктор

Классы 219

//

};

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

{

cout << ”\nРаботает деструктор для ”;

// Сообщаем о себе,

Print();

 

// выводим дату

}

 

 

int main()

 

 

{

 

 

Date Today;

 

// Сегодня

{

 

// Начало блока

Date Rev(7, 11, 1917);

// Создание локальной переменной в блоке

}

// Конец блока. Rev уничтожается

return 0;

 

 

}

 

// Здесь уничтожается Today

Программа выдает:

Работает деструктор для 7.11.1917 Работает деструктор для 19.4.2006

Данный пример подтверждает, что деструктор действительно вызывается неявно при уничтожении объектов класса.

Программа 49. Многоугольники

В программе разработан класс Plgn для моделирования многоугольников на плоскости. В конструкторе этого класса динамически выделяется память под координаты вершин, а в деструкторе освобождается память, выделенная в конструкторе. В классе предусмотрены функции – члены для изображения многоугольника на экране, для его стирания с экрана, а также для перемещения и вращения многоугольника.

При решении задачи надо решить вопрос о совмещении математической плоскости, с помощью которой мы будем моделировать многоугольник, с физической плоскостью экрана. На рис.69 показана схема экрана и действующая на нем физическая экранная система координат xs, ys. Совместим начало математической системы координат x, y с центром экрана (x0, y0). Будем считать, что математическая плоскость отображается на плоскость экрана в масштабе 1:1, то есть математическая единица длины соответствует расстоянию между соседними пикселями на экране. Приведенные соображения реализованы в программе. В функциях рисования нужны физические

220 15

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

xs = x0 + x, ys = y0 – y.

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

0

y

 

x[1]

639

xs

0

 

 

 

 

 

 

 

 

 

 

 

 

x0

1

 

 

 

 

y[1]

 

 

0

 

 

 

 

 

 

3

 

 

 

 

 

 

x

 

 

 

 

 

 

 

 

 

 

 

y0

 

 

 

 

2

 

 

 

 

 

 

5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4

 

 

 

 

 

 

 

 

479

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

экран

 

 

 

 

ys

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис.69. Плоскость экрана и математическая система координат

// Файл Polygon.cpp

 

 

 

 

 

 

 

 

class Plgn

 

 

// Класс многоугольников

{

 

 

 

 

 

 

 

 

 

 

int n;

 

 

// Число вершин

 

 

float *x;

 

 

// Указатель на массив абсцисс

float *y;

 

 

// Указатель на массив ординат

static int x0, y0;

 

 

// Координаты центра экрана

public:

 

 

 

 

 

 

 

 

Plgn(int n = 3);

// Конструктор с аргументом по умолчанию

~Plgn();

// Деструктор

 

 

 

 

void Show();

// Функция рисования многоугольника

void Hide();

// Функция, убирающая изображение

void Move(int dx, int dy);

// Смещение многоугольника на dx, dy

void Rotate(float angle);

// Поворот на угол angle градусов

 

 

void Movement();

// Движение, пока не нажата Esc

 

 

static void SetBegCoord(int xn, int yn)

// Установка координат центра

{ x0 = xn; y0 = yn; }

 

 

 

 

 

 

 

 

};

 

 

 

 

 

 

 

 

 

 

# include <iostream.h>

 

Классы

221

# include <graphics.h>

// Графика

 

# include <stdlib.h>

// Доступ к random

 

# include <conio.h>

 

 

# include <math.h>

 

 

Plgn::Plgn(int nn)

// Конструктор

 

{

 

 

n = nn;

 

 

x = new float [n];

// Выделение памяти под массив абсцисс

y = new float [n];

// Выделение памяти под массив ординат

for(int i = 0; i < n; i++){

// Заполнение массивов координат

 

x[i] = x0 - random(x0 * 2);

// случайными числами

 

y[i] = y0 - random(y0 * 2);

 

 

}

 

 

}

 

 

Для простоты координаты вершин многоугольника задаются в конструкторе случайными числами с использованием функции

int random(int num);

которая генерирует случайное целое число из диапазона от 0 до num – 1; Таким образом, случайные координаты вершин многоугольника будут находиться в диапазонах [-x0, x0] и [-y0, y0] и, следовательно, не выйдут за пределы экрана.

Plgn::~Plgn()

// Деструктор

{

 

delete [] x;

// Освобождение

delete [] y;

// памяти

}

 

void Plgn::Show()

// Рисует многоугольник, рядом

{

// с вершиной выводит ее номер

char num[20];

// Память под номер вершины

for(int i = 0; i < n; i++){

 

line(x0 + x[i], y0 - y[i],

// Изображение

x0 + x[(i + 1) % n], y0 - y[(i + 1) % n]); // i-й стороны

itoa(i, num, 10);

// Получение номера вершины

outtextxy(x0 + x[i], y0 - y[i], num);

// Вывод номера вершины

}

 

}

 

Функция line рисует отрезки, соединяющие соседние вершины. Возле вершин многоугольника выводятся их номера. Для вывода текста на экран в графическом режиме использована функция

void outtextxy(int x, int y, char *textstring);

222 15

которая выводит строку символов textstring, начиная с пикселя экрана с координатами x, y.

Для получения строки с номером вершины использована функция (заголовочный файл stdlib.h)

char *ltoa(long value, char *string, int radix);

которая преобразует число value в строку цифр string, дающую представление числа в системе счисления с основанием radix.

void Plgn::Hide()

 

// Стереть изображение

{

 

 

int clr = getcolor();

 

// Запоминаем цвет рисования

setcolor(getbkcolor());

 

// Цветом рисования делаем цвет фона

Show();

// Рисуем цветом фона, изображение исчезает

setcolor(clr);

 

// Восстанавливаем прежний цвет рисования

}

 

 

void Plgn::Move(int dx, int dy) // Смещение многоугольника на dx

{

 

// по горизонтали и на dy по вертикали

Hide();

 

// Убираем изображение

for(int i = 0; i < n; i++)

// Смещение

x[i] += dx, y[i]+=dy;

// каждой вершины

Show();

 

// Снова показываем многоугольник

}

 

 

Выпишем формулы, нужные для программирования поворота многоугольника вокруг центра масс. На рис.70 показан многоугольник и одна из его вершин M в исходном положении и после его поворота вокруг точки C на угол β. Новое положение вершины обозначено M1.

В качестве центра масс можно взять точку C с координатами:

n

n

 

xc = å xi

n , yc = å yi

n

i=1

i=1

.

Здесь n – число вершин многоугольника, xi, yi, i=1,…, n – координаты вершин. Данная формула действительно будет давать координаты центра масс, если вся масса многоугольника сосредоточена в его вершинах и массы вершин одинаковы.

Как видно из рис.70, координаты точки M относительно центра масс C равны:

xr = x xC = CM cosα , yr = y yC = CM sinα .

Классы 223

Рис.70. Поворот многоугольника вокруг центра масс

Так как CM1 = CM , то относительные координаты точки M1 после поворота выразятся формулами:

xr1 = CM1 cos(α + β ) = CM1 cosα cos β − CM1 sinα sin β = = xr cos β − yr sin β ,

yr1 = CM1 sin(α + β ) = CM1 sinα cos β + CM1 cosα sin β =

=yr cos β + xr sin β .

Таким образом, новые абсолютные координаты точки M будут:

x1 = xC + xr1 = xC + xr cos β − yr sin β , y1 = yC + yr1 = yC + yr cos β + xr sin β .

Эти формулы используются в функции вращения многоугольника:

void Plgn::Rotate(float angle)

// Функция поворота многоугольника

{

// на угол angle (задается в градусах)

float xC = 0, yC = 0, xr, yr;

 

int i;

 

angle = angle*M_PI/180;

// Перевод угла в радианы

Hide();

// Убираем изображение

for(i = 0; i < n; i++){

// Вычисление координат

xC += x[i]; yC += y[i];

// центра масс

}

 

xC /= n; yC /= n;

 

224 15

for(i = 0; i < n; i++){ // Получение координат вершин после поворота

xr = x[i] - xC;

// Координаты вершины

yr = y[i] - yC;

// относительно центра масс

x[i] = xC + xr * cos(angle) - yr * sin(angle);

// Координаты вершины

y[i] = yC + xr * sin(angle) + yr * cos(angle);

// после поворота

}

 

 

Show();

// Показываем многоугольник

}

 

 

void Plgn::Movement()

// Движение при нажатии клавиш

{

// со стрелками, пока не нажата Esc

const int UP = 72, DOWN = 80, LFTUP = 76,

// Константы

RIGHT = 77, LEFT = 75, ESC = 27;

// для кодов клавиш

char c = 32; // Начальное значение для c должно отличаться от ESC

while(c != ESC){

// Пока не нажата клавиша esc

c = getch();

// Читаем код клавиши

if(c == 0)

// Если у клавиши двухбайтовый код,

c = getch();

// читаем второй байт

switch(c){

 

 

case LEFT: Move(-1, 0); break;

// Нажата стрелка влево

case RIGHT: Move(1, 0); break;

// Нажата стрелка вправо

case UP: Move(0, 1); break;

// Нажата стрелка вверх

case DOWN: Move(0, -1); break; // Нажата стрелка вниз case LFTUP: Rotate(5); break;

}

}

}

Перемещение многоугольника производится нажатием клавиш со стрелками. Вращение – нажатием клавиши 5 на дополнительной

цифровой клавиатуре. При этом не должна гореть лампочка NumLock,

чтобы клавиши дополнительной цифровой клавиатуры работали как управляющие, а не как цифровые.

// Определение статических членов класса – координат центра экрана int Plgn::x0, Plgn::y0;

void main()

{

int gd = DETECT, gmode, n, x; char c;

randomize(); // Инициализация датчика случайных чисел initgraph(&gd, &gmode, "C:\\TC30\\BGI"); // Установка граф. режима

// Задаем координаты центра экрана Plgn::SetBegCoord(getmaxx() / 2, getmaxy() / 2);

cout << "Введите число вершин: ";

 

cin >> n;

// Ввод числа вершин

 

Классы

225

Plgn mnog(n);

// Многоугольник

 

setcolor(WHITE);

// Установка цвета

 

mnog.Show();

// Вывод многоугольника

 

mnog.Movement();

// Перемещения многоугольника

mnog.Hide();

// Убираем многоугольник

 

Plgn triangle;

// Многоугольник по умолчанию (треугольник)

triangle.Show();

// Показываем треугольник

 

triangle.Movement();

// Движение треугольника

 

triangle.Hide();

// Скрываем треугольник

 

closegraph();

// Переход в текстовый режим

}

Вызов статической функции

Plgn::SetBegCoord(getmaxx() / 2, getmaxy() / 2);

устанавливает для статических членов класса Plgn::x0, Plgn::y0 значения координат центра экрана.

В главной функции сначала создается многоугольник mnog, число вершин которого следует ввести. Затем для mnog вызывается функция Movement, которая позволяет его двигать и вращать. Далее создается треугольник triangle с использованием конструктора по умолчанию. Треугольником также можно «поиграть».

Для чтения кодов нажимаемых клавиш использована функция int getch() (заголовочный файл conio.h), которая возвращает для обычных алфавитно-цифровых клавиш однобайтовый код, равный коду соответствующего символа, а для управляющих и функциональных клавиш – двухбайтовый код. Для таких клавиш при первом вызове getch() возвращает 0, а при втором – некоторый код, свой для каждой клавиши. Коды, возвращаемые getch(), можно узнать с помощью программы:

//Файл CodeKeys.cpp

//Получение кодов клавиш функцией getch() #include <iostream.h>

#include <conio.h> void main()

{

cout << "\nНажмите клавишу\n"; int c = getch();

if(0 == c){

c = getch();

cout << "\nУ клавиши двухбайтовый код: 0 " << c << endl;

}

else

cout << "\nУ клавиши однобайтовый код: " << c ;