Раздел «Язык Си».OOP-Instrumental:

Понятие объекта и класса

Простые объекты

Объектно-ориентированный подход предполагает, что в работа программы определяется взаимодействием различных объектов в ответ на обращение к каждому из них.

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

Можно представить, что все преременные, которые используются в алгоритмах - это простейшие объекты, к которым обращается <<высший разум>> через действия алгоритма.

В C++ сущность объектов определяется через описание класса объектов. Класс позволяет породить хотя бы один объект. Если удалось получить хотя бы один объект, можно получить сколько угодно других (пока позволяют ресурсы).

При этом все объекты имеют свой собственный набор атрибутов и методов. Метод — это функция, которая принадлежит непосредственно объекту и имеет доступ ко всем атрибутам этого объекта.

Объект обладает:

  1. идентичностью,
  2. набором атрибутов, значения которых определяет состояние объекта,
  3. и поведением, которе опрделяется методами, присущими объекту.

Совокупность объектов, обладающих одинаковым набором атрибутов и поведением определяет общий для них класс или понятие.

В С++ сущность объектов определяется через описание класса подобных объектов. Класс позволяет породить хотя бы один реальный объект. Если удалось породить хотя бы один объект, то можно получить сколько угодно других объектов (если позволяют ресурсы)

Рассмотрим простейшую задачу.

REFACTOR Задача.

Время задается часами и минутами, которые отображаются на 24-часовом циферблате. Первоначально хотелось бы иметь такой инструмент, который:

  1. для новых часов устанавливал бы их в 00:00
  2. отображал бы время, указанное на 12-часовом циферблате в показаниях этих часов.

Понятно, что нам нужен объект, у которого есть атрибут <<час>> и атрибут <<минуты>>. Такой объект вполне можно описать используя структуры языка С и набор функций .

// Структура для описания показаний часов
typedef struct{
  int h; // часы
  int min; // минуты
}Time;
// установка часов в 0:0
void setTimeZero(Time*);
// Преобразование к 12-часовому циферблату
Time convertTo12(int h24, int min24);
// Печать показаний часов
void printTime(Time);

int main(){
  Time t1,t2;
// t1 - в 0:0
  setTimeZero(&t1);
//  
  t2 = convertTo12(23,55);
  printTime(t2);
}

При объявлении переменных t1 и t2 выделяется память для всех атрибутов (h,min). Инициализвция атрибутов не происходит, то есть значения их - случайные числа. При этом существует опасность установки значений для h и min не соответсвующих показаниям наших часов. Работа программы при этом становится непредсказуемой.

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

В языке С++ используется несколько иной подход. Для описания объектов используются как структуры, так и классы.

Рассмотрим описание объекта через class.

В С++ возможно:

  1. определить для каждого объекта не только атрибуты, но и методы - его собственные функции, которые будут работать только с атрибутами данного объекта.
  2. ограничить доступ к отдельным атрибутам или функциям, для того, чтобы обезопасть их значения от неразумного или вредоносного изменения.
  3. описать специальные функции <<конструктор>> и <<деструктор>> для управления созданием и удалением каждого объекта.
  4. и др.

Рассмортрим простейший вариант описания часов. В данном примере описание объекта(интерфейс) и реализация методов-функций приводятся в одном файле для простоты.

//*********** Мантра***************************
// Подключение заголовочного файла для ввода/вывода 
#include <iostream>
// подключение стандартной библиотеки
#include <cstdlib>
//______заклинание_____________________________
// смысл заклинание будет ясен позже
using namespace std;
//******* конец мантры **********************

// Описание интерфейса класса Time
class CTime{

// по-умолчанию, атрибуты считаются недоступными 
// пользователю. К ним можно обращаться через 
// функции, которые будут объявлены доступыми
  int h;// часы, от 0 до 11
  int min; // минуты, от 0 до 59
  
// объявление открытой области:
// все атрибуты и функции из этой области 
// доступны пользвателю объекта
 public:

// Объявление конструктора
   CTime();
// Конструктор всегда имеет имя класса.
// По умолчанию конструктор просто выделяет необходимую 
// память  для атрибутов и методов объекта
// но конструктор - тоже функция, и можно
// добавить в него свой код

// объявление методов:
  void setTime(int, int); // установка времени
  void print(); // печать значений часов
  CTime add(CTime);// получить сумму времен
 
/* ***************************************** 
Все методы-функции класса имеют доступ  к закрытой 
области класса. И в каждом объекте методы работают
только со "своими" атрибутами. Именно поэтому 
в методах нет надобности передавать в качестве 
параметра объект как это было в С
***********************************************/
};

//_________ Реализация функций____________________
/*
  Каждый атрибут или метод имеет короткое имя в своем классе
  или полное имя. 
  Для класса CTime, полное имя метода или атрибута получается
  приставкой имени класса перед именем атрибута или метода:
  CTime::CTime() - конструктор, CTime::print() и т.д.
*/

// Конструктор.
  CTime::CTime(){
// конструктор вызывается при создании объетка.
// Пусть атрибуты сразу будут равны 0
  h = 0;
  min = 0;
};

//  печать значений атрибутов
void CTime::print(){
 cout<<h<<':'<<min<<endl;
};

// Установка времени. Будем считать, что минуты не могут 
// быть меньше 0 и больше 59, и часы должны быть не меньше 0
void CTime::setTime(int h24, int min24){
// сразу проверяем допустимость значений
// если не правильно - прерываемся
   if(0 > min24 || 59 < min24 || 0 > h24 ){
     cout<<"Неверное время\n";
     exit(1);
   }
   min = min24;
// часы >0 конвертируем.
  h = h24 % 12; 
};
// Сумма времен
// Результатм является объект типа CTime
CTime CTime::add(CTime a){
// Чтобы вернуть объект, его нужно иметь
// Создадим временный объект
   CTime tmp;
// так как объекты a и tmp - это объекты класса CTime
// все функции этого класса имеют дрступ ко всем атрибутам этого класса
// Значит эта функция CTime::add() может обращаться к закрытым атрибутам
// объетка a и tmp
   int mins = min + a.min;
   tmp.min = mins % 60;
   tmp.h = (h + a.h + mins / 60) % 24;
  
   return tmp;  
};
  
// Как использовать объект часы:
int main(){
// Объявление объекта
  CTime t1, t2; // h,min сразу 0
  CTime res; // для суммы
// Доступ к методам как к атрибутам в структуре
// Для конкретного объекта t1 устанавливаются 
// значения ЕГО атрибутов
  t1.setTime(10,20);
// печать атрибутов t1  
  t1.print();
  
  t2.print();
    res = t1.add(t2);

} 

Итого наш "суперразум" создал часы (даже двое), установил время на часах и распечатал его.

REFACTOR Задачи

REFACTOR Задача 1.

Для класса CTime добавить следующие функции:

CTime diff(CTIme); // возвращает объект время - на сколько различается текущее время и время аргумента

// возвращает 1, если текущее время на циферблате больше аргумента, 0 - если совпадают
// и -1, если меньше.
int isLater(CTime);
Проверить и отладить функции

REFACTOR Задача 2.

Сначала у Шляпсика и Бяксика были нормальные одинаковые часы. Когда они показывали время t0, Шляпсик решил переводить их на t1 минут вперед каждые 30 минут, а Бяксик стал переводить из на t2 минут назад каждый час

Написать программу, которая выясняет через сколько суток, часы Шляпсика и Бяксика покажут одинаковое время и какое это будет время.

REFACTOR Задача 3.

На координатной плоскости у нас есть точки. Каждая точка задана координатами x и y.

Опишем класс Point для этих точек.

// Начало мантры
#include <iostream>
#include <cstdlib>
using namespace std;
// Конец мантры

// Класс Point
class Point{
  int x,y;
public:
    Point();
 // Получить значения с клавиатуры
    void getVal();
// Переместить точку вдоль X на x1, вдоль Y на y1
    void moveOn(int x1, int y1);
// Вычислить на сколько нужно передвинуть точку по X до точки а
    int diffX(Point a);
// Вычислить на сколько нужно передвинуть точку по Y до точки а
    int diffY(Point a);
// печать
    void print();
};

Реализовать все функции объекта (отдельный файл реализации), проверить их и поместить в библиотечный файл libfig.a (раздел Понятие проекта )

Как взаимодействуют объекты

Объект может:

  1. передавать сообщения обращаясь к методам и атрибутам другого объекта (если есть доступ)
  2. порождать другие объекты
  3. объект может быть частью другого объекта
  4. удалять порожденные объекты, к которым он имеет доступ

Рассмотрим некоторые примеры.

Прямоугольники имеют стороны параллельные осям координат.

Класс Point

// Начало мантры
#include <iostream>
#include <cstdlib>
using namespace std;
// Конец мантры

// Известный уже класс Point
// Вершины треугольника - точки на плоскости
class Point{
  int x,y;
public:
    Point();
    Point(int x1, int y2);
// Получить значения с клавиатуры
    void getVal();
// Переместить точку вдоль X на x1, вдоль Y на y1
    void moveOn(int x1, int y1);
// Вычислить на сколько нужно передвинуть точку по X до точки а
    int diffX(Point a);
// Вычислить на сколько нужно передвинуть точку по Y до точки а
    int diffY(Point a);
// печать
    void print();
};

Прямоугольник задается двумя точками: верхняя левая и правая нижняя.

Значит, в описании прямоугольника должен участвовать класс Point. То есть в объект типа прямоугольник будет иметь атрибуты типа точка.

Опишем класс Прямоугольник.

class Rect{
 Point hl,lr;// точки левая верхняя и правая нижняя
 Point center;// точка пересечения диагоналей.
public:
 Rect();// конструктор

// Деструктор. (Всегда называется как класс, но перед именем ставится ~)
// Удаляет созданный объект: 
// 1. командой delete
// 2. если объект появился в функции, когда завершается функция
// 3. когда завершается программа
 ~Rect();

 void setPoints(Point,Point);//задаем точки 
 void print(); // печать атрибутов
 void moveToPoint(Point а); // пермещение прямунольника в точку а (центр)
 void mirrorV(); // отображение прямоугольника относительно оси OY
 void mirrorH(); // отображение прямоугольника относительно оси OX
 void rotate90();// правый поворот на 90гр. относительно центра
 Rect* crossRect(Rect a);//вернуть прямоугольник — пересечение с прямоугольником а
}
// Реализация 
Rect::Rect(){
// здесь нужно написать код
};

// Деструктор. 
// Обычно работает "деструктор по-умолчанию", так же как и 
// "конструктор по-умолчанию", но можно добавить в деструктор и 
// другие действия 
Rect::~Rect(){
   cout<<"Работает деструктор"<<endl;
};

void Rect::setPoints(Point a, Point b){
// здесь нужно написать код
};
void Rect::print(){
// здесь нужно написать код
};

//Рассмотрим реализацию перемещения прямоугольника в указанную точку
// Заметим, что атрибуты x и y находтся в закрытой области класса Point
// Поэтому мы не можем просто так совершить с ними желаемые действия.
// Придется пользоваться теми функциями класса Point, которые он предоставляет
void Rect::moveToPoint(Point a){
// будем двигать сначала точку hl, а затем lr
// Вычислим на сколько нужно их двигать 
   int dx = center.diffX(a);
   int dy = center.diff(a);

// Воспользуемся функцией moveOn для точек:
   hl.moveOn(dx,dy);
   lr.moveOn(dx,dy);
// про центр не забываем
   center.moveOn(dx,dy);    

// функции класса Point позволили нам совершить необходимы действия в
// "безопасном" для класса режиме.
};

//Реализация crossRect(Rect a)
// функция должна возвращать указатель на новый прямоугольник,
// который может быть создан в процесе ее работы
// может быть так, что такого прямоугольника не существует
// тогда функция вернет 0
Rect* Rect::crossRect(Rect a){
// здесь нужно написать код для выяснения точек пересечения
// создание прямоугольника
  if(/*прямоугольника нет (не пересекаются)*/)
// возвращаем 0
    return 0;
  else /* получился*/{
// создаем указатель на прямоугольник и
// создаем ДИНАМИЧЕСКИЙ объект
   Rect *tmp;
// оператор new создает синамические объекты и возвращает указатель на них
   tmp = new Rect();  
  }  
  return tmp; 
};
// отображение прямоугольника относительно оси OY
void Rect::mirrorV(){
// здесь нужно написать код
}; 
 // отображение прямоугольника относительно оси OX
void Rect::mirrorH(){
// здесь нужно написать код
};
// правый поворот на 90гр. относительно центра
void Rect::rotate90(){
// здесь нужно написать код
};

//Пример как использовать динамические объекты
int main(){
  Rect *prect = 0,pr1;
  prect = new Rect();// создали
  Point a,b,c,d;
  a.getVal();
  b.getVal();
  c.getVal();
  b.getVal()
  prect->setPoint(a,b);// обращение через указатель
  preсt-mirrorV();
  prect->print();
  pr1.setPoint(c,d);
  Rect *cross = 0;
  cross = prect->crossRect(pr1);
 
// Проверка: может быть cross==0
   if(cross ){
    cross->print();
// не нужен больше, удалим
// оператор delete вызывает деструктор
    delete cross;
    cross = 0;//!!!!!!
   }
// массив из Point
  Point *tochki = new Point[3];
// что-то с ними делали

// не нужны, удалим
  delete[] tochki;
  tochki = 0;//!!!!!

}

REFACTOR Задача 4.

Реализовать и проверить все функции класса Rect. Использовать библиотеку . Добавить функции класса Rect в библиотечный файл libfig.a.

REFACTOR Задача 5.

Продумать и реализовать функциональность класса Line. Использовать его для нахождения пересекающихся прямоугольников. Добавить функции класса Line в библиотечный файл libfig.a.

REFACTOR Задача 6 (!).

Даны N точек. Создать динамический массив всех возможных прямоугольников из этих точек. Прямоугольник задается парой точек (левая верхняя и правая нижняя). Стороны прямоугольников параллельны осям координат. Подсчитать сколько всего прямоугольников будет видно (учитывая пересечения). Использовать библиотеку libfig.a.

-- TatyanaOvsyannikova2011 - 22 Sep 2015