Лекція 7. Класи

0

Posted by Виктория Павловна Дончик | Posted in Новости | Posted on 09-09-2011

Лекція 7. КласиЛекція 7. Класи

План

1. Синтаксис класу.

2. Визначення методів класу. 5

3. Вказівник this. 6

Контрольні питання. 8

Література. 8

Мотивація:

Клас мови С++ дуже схожий на структуру.

Структури мови C дозволяють згрупувати набір зв'язаних змінних-членів.

Приклад 1. Створимо прямокутник, зберігатимемо його координати у вигляді структури, визначеної таким чином:

struct Rectangle

{

int Left;

int Top;

int Right;

int Bottom;

};

Далі можна визначити функцію малювання прямокутника.

void DrawRectangle(Rectangle Rect)

{

Line (Rect.Left, Rect.Top, Rect.Right, Rect.Top);

Line (Rect.Right, Rect.Top, Rect.Right, Rect.Bottom);

Line (Rect.Right, Rect.Bottom, Rect.Left, Rect.Bottom);

Line (Rect.Left, Rect.Bottom, Rect.Left, Rect.Top);

}

В даному прикладі Line - гіпотетична (т.е. вигадана) функція, яка дозволяє малювати лінію від крапки, заданої першими двома координатами, до крапки, визначеної другими двома координатами. Така функція може бути визначена де-небудь в програмі або викликана з бібліотеки функцій. Нарешті, щоб задати прямокутник у визначеному місці, потрібно визначити і ініціалізувати змінну типа Rectangle, а потім передати її у функцію DrawRectangle.

Rectangle Rect={25,25,100,100};

DrawRectangle(Rect);

У С++ можна визначити новий тип даних за допомогою конструкцій мови. Клас в С++ - це структурований тип, утворений на основі вже існуючих типів. У цьому сенсі клас є розширенням поняття структури.

1. Синтаксис класу

Клас можна визначити за допомогою конструкції

тип_класу им’я_класу {компоненти класу};

  • тип класу – одно з службових слів class, struct, union;
  • им’я_класу – ідентифікатор (задається користувачем);
  • компоненти класу – визначення і об’явлення даних, що належать класу функцій.

Функція, що є компонентом класу, називається методом класу або функцією-членом. Дозволяється визначати клас:

о    з полями і методами

о    тільки з полями

о    тільки з методами

о    без полів і без методів.

Клас без полів і без методів називається порожнім класом.

Елементи класу типу struct за умовчанням відкриті і доступні для інших частин програми. Члени класу class за умовчанням закриті і недоступні поза даним класом. Доступ до вмісту класу задається специфікаторами доступу, які позначаються public: і private:

public:  - об’явлення елемента класу, до якого мають доступ поза класом;

private: - закриває доступ до елементу класу зовні.

Можно писать столько спецификаторов public и private, сколько необходимо, и в том порядке, в котором необходимо. Очередной спецификатор действует до следующего.

Відкрита частина класу називається інтерфейсом. Заховання інформації про внутрішню структуру – це один з принципів об'єктно-орієнтованого програмування, так звана інкапсуляція.

Основна форма класу:

class им’я_класу

{   //закриті функції і змінні класу

public:

//відкриті методи

};

Приклад 2. Визначимо клас, у якому всі поля і методи закриті:

class CRectangle

{

int Left;

int Top;

int Right;

int Bottom;

void DrawRectangle()

{

Line (Left, Top, Right, Top);

Line (Right, Top, Right, Bottom);

Line (Right, Bottom, Left, Bottom);

Line (Left, Bottom, Left, Top);

}

};

Змінна класу називається об'єктом. Об'єкти оголошуються таким чином:

им’я_класу им’я_об’єкта;                                   //скалярний об’єкт

им’я_класу *им’я_об’єкта;                      //вказівник

им’я_класу им’я_об’єкта[кількість];       //масив


Дозволяється суміщати визначення класу і оголошення змінній:

тип_класу им’я_класу

{

компоненти класу

} им’я_об’єкта (*им’я_об’єкта або им’я_об’єкта[кіл]);

Пример 3.

CRectangle Rect;               // bluza -скалярна змінна

CRectangle *ptr;                //  *ptr – об’явлення вказівника

CRectangle mas[10];          //   mas[10] – масив об’єктів

Розглянемо визначення:  CRectangle Rect;

Це визначення створює екземпляр класу CRectangle, який називають об'єктом. Екземпляр Rect класу CRectnagle займає власний блок пам'яті і може використовуватися для зберігання даних і виконання операцій над ними. Як і змінна вбудованого типу, об'єкт існує, поки потік управління не виходить за межі області видимості його визначення (наприклад, якщо об'єкт визначений усередині функції, то він знищується при виході з неї). Срочатку повинне бути визначення класу, а потім визначення і використання екземпляра класу. Відмітимо, що можна створити довільне число екземплярів даного класу.

Доступ до відкритих полів :

  • об'єкт . поле;
  • покажчик -> поле;
  • ім'я _ масиву [ індекс ]. поле.

Виклик відкритих методів:

  • об'єкт . метод ( параметри );
  • покажчик -> метод ( параметри );
  • ім'я _ масиву [ індекс ]. метод (параметри).

Приклад 4. З використанням специфікатора доступу public створимо відкритий член класу, доступний для використання всіма функціями програми (як усередині класу, так і за його межами).

Розглянемо варіант класу CRectangle, де всі члени є відкритими

class CRectangle

{

public:

int Left;

int Top;

int Right;

int Bottom;

void DrawRectangle()

{

Line (Left, Top, Right, Top);

Line (Right, Top, Right, Bottom);

Line (Right, Bottom, Left, Bottom);

Line (Left, Bottom, Left, Top);

}

};

Специфікатор доступу застосовується до всіх членів, розташованих після нього у визначенні класу (поки не зустрінеться інший специфікатор доступу).

Тепер, коли всі члени класу CRectangle відкриті, доступ до них можливий з використанням оператора "." (крапка) .

CRectangle Rect;         //визначення об’кту CRectangle

Rect.Left=5;                //присвоєння значення змінній – членам

Rect.Top=10;

Rect.Right=100;

Rect.Bottom=150;

Rect.Draw();               //створення прямокутника

ЗАУВАЖЕННЯ до прикладу 4. Згідно принципам інкапсуляції внутрішні структури даних, використовувані в реалізації класу, не повинні бути доступні користувачеві класу безпосередньо. Проте наша остання реалізація класу CRectangle явно порушує цей принцип, оскільки користувач може безпосередньо читати або модифікувати будь-які змінні-члени. Щоб не порушувати правила заховання даних клас СRectangle повинен бути визначений так, щоб мати доступ до функцій-членів (у нашому прикладі - це функція Draw()), але не мати доступу до внутрішніх змінних-членів, використовуваних цими функціями (Left, Top, Bottom, Right)

Приклад 5.

class CRectangle

{

private:

int Left;

int Top;

int Right;

int Bottom;

public:

void DrawRectangle()

{

Line (Left, Top, Right, Top);

Line (Right, Top, Right, Bottom);

Line (Right, Bottom, Left, Bottom);

Line (Left, Bottom, Left, Top);

}

};

Проаналізуємо приклад 5. Специфікатор доступу private робить змінні, визначені пізніше, закритими. Таким чином вони доступні тільки функціям-членам класу. Подібно до специфікатора доступу public, специфікатор private впливає на всі оголошення, що стоять після нього, поки не зустрінеться інший специфікатор. Отже, таке визначення робить змінні Left, Top, Right, Bottom закритими, а функцію Draw() - відкритою.

Напевно, Ви здогадалися, що специфікатор доступу private, не потрібно поміщати на початку визначення класу, тому що члени класу за умовчанням є закритими.

Проте включення специфікатора private полегшує читання програми.

Приклад 6. Наступний код ілюструє як коректне, так і некоректне звернення до членів чергового варіанту класу CRectangle.

void main()

{

CRectangle Rect;   //Визначення об’єктаCRectangle

Rect.Left=5;            //Помилка! Не має доступа до ЗАКРИТОГО члена

Rect.Top=5;            //Помилка!

Rect.Draw();           //можна використати, але координати не визначені!

}

Приклад 7:

Тепер, коли користувачеві класу заборонений прямий доступ до змінних-членів, клас повинен надати альтернативний засіб вказівки координат перед створенням прямокутника. Хороший спосіб для цього - надання відкритої функції-члена, що приймає необхідні значення координат і що використовує ці значення для установки змінних-членів.

void SetCoord (int L, int T, int R, int B)

{

Left=L;

Right=R;

Top=T;

Bottom=B;

}

Додамо цю функцію в розділ public визначення класу СRectangle. Тепер клас CRectangle можна використовувати для створення прямокутника.

void main()

{

CRectangle Rect; //Определение объекта CRectangle

Rect.SetCoord(30,30,100,100); //установка координат прямоугольника

Rect.Draw();  //отображение прямоугольника

}

Напрошується наступне питання: "Може простіше було б "просто" привласнити членам-змінним значення і не мучитися?". "Простіше" - можливо, а ось проблем було б ще більше! Очевидна перевага інкапсуляції полягає в тому, що вона дозволяє розробникові перевірити правильність будь-яких значень, що привласнюються змінним-членам, і тим самим запобігти помилкам програмування! Інша перевага управління доступом до внутрішніх структур даних полягає в тому, що автор класу може вільно змінити спосіб представлення цих даних, не змінюючи інші частини програми, що використовують клас.

2. Визначення методів класу

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

Приклад 8.

Визначення методу у середині класу.

class tovar

{ float first;

int second;

public:

float cost(float f,int c) {return f*c;};

};

Визначення методу поза класом

class tovar

{ float first;

int second;

public:

float cost(float f,int c);

};

float tovar::cost(float f,int c)

{return f*c;}

Якщо метод визначається поза класом, то приналежність методу класу указується префіксом-ім'ям класу:

тип _ що повертається _ знач ім'я _ класу :: ім'я _ методу (формальні параметри)

{тіло функції (методу)}

Приклад 9. Поле  first –дробное позитивне число, ціна товару, second – ціле покладе. число,  кількість товару. Реалізувати метод cost() – обчислення вартості товару.

#include <iostream.h>

#include <conio.h>

class tovar

{ float first;

int second;

public:

float cost(float f,int c);

};

void main()

{ clrscr();

tovar bluza,*ptr;

cout<<bluza.cost(10.5,2)<<endl;

cout<<ptr->cost(5.5,2);

getch();

}

float tovar::cost(float f,int c)

{return f*c;}

3. Вказівник this

Звернення до даних класу здійснюється за допомогою функцій, які можуть викликатися різними об'єктами класу. Виникає питання: звідки функція “знає”, який об'єкт його викликав і які дані при цьому повинні бути використані?

Наприклад, в прикладі 9 виклики функцій bluza.cost(10.5,2) і ptr->cost(5.5,2)приводять до висновку різних для first і second значень. Це відбувається унаслідок того, що кожен об'єкт має прихований покажчик this на саме себе, оголошуваний неявно. Значенням цього покажчика, який автоматично передається функціям-членам класу при їх виклику, є адреса початку об'єкту. В результаті функція cost () у реальності містить інструкцію:

{return this ->f* this ->c;}

При цьому *this представляє собою об’єкт, який має посилання на дані об'єкту класу.

this->им’я_члена

Приклад 10:

#include<iostream.h>

#include<conio.h>

class MyClass {

int x,y;

public:

void set(int xx, int yy) {                                            //встановлює дані

this->x=xx;

this->y=yy;}                                                   //це все одно, що  x=xx;  y=yy;

MyClass getObject(MyClass &);                            //повертає об’єкт

MyClass *getPointer(){ /*повертає вказівник на об’єкт, що викликав */

x=y=100;

return this;}                                                     //використання вказівника this

void display(){cout<<x<<'\t'<<y<<endl;}

};

MyClass MyClass:: getObject(MyClass &M)

{x+=M.x; y+=M.y;

return *this;}

int main(){

MyClass a, b;

a.set(10,20);

b. getPointer()->display();        //результат 100,100

b.display();                              //результат 100,100

a.display();                   //результат 10,20

a.getObject(b).display();          //результат 110,120

a.display();                   //результат 110,120

while (!kbhit());

return 0;

}

Оскільки функція b.getPointer() повертає покажчик this на об'єкт b, то можна викликати функцію display() як this->display(). При цьому виводяться поля даних об'єкту b(100,100). При  виклику а.getObject(b).display() функція getObject(b) повертає значення об'єкту, що викликав її, а як *this. Потім викликається функція display() як а.display().

Відзначимо, що у функцію краще передавати не значення об'єкту, а посилання на нього. Це дає економію стекової пам'яті, оскільки об'єкт не копіюється, і безпечніше для об'єктів, що використовують  динамічну пам'ять. Знищення копій аргументів при виході з функції не може зруйнувати такі об'єкти при звільненні динамічної пам'яті.

Функції-друзі класу, що оголошуються із специфікатором friend, покажчик this не містять. Об'єкти, з якими працюють такі функції, повинні передаватися як їх аргументи.


Контрольні питання

  1. Що таке клас, як його визначити?
  2. Які спеціфікатори знаєте? Для чого вони використовуються?
  3. Прокоментувати:

class date

{ int month, day;

public:

void set (int day, int m);

};

  1. Як об’явити змінні типа «ім’я класу»?
  2. Прокоментува tovar  *ptr.
  3. Прокоментувати tovar  mas[10].
  4. Як визначити метод класу?
  5. Прокоментувати:

float tovar::cost(float f,int c)

{return f*c;}

  1. Як звернутися до методу класу ?
  2. Прокоментувати:

bluza.cost(10.5,2);

ptr->cost(5.5,2);

  1. Як визвати метод, якщо дан масив об’єктів?
  2. Чому створено методи, а не присвоюються значення членам – змінним (полям класу)?

Література

1. Глушаков С.В., Коваль А.В., Смирнов С.В., Язык программирования С++. - Харьков «Фолио», 2003

2. Глинський Я.М., Анохін В.Є., Ряжська В.А.  С++ і С++ Builder. – Львів: Деол, СПД Глинський, 2004.

3. Павловская Т. А., C/C++. Программирование на языке высокого уровня /. — СПб.:Питер, 2003. —461 с:

4. Шилдт Г. Самоучитель С++: Пер. С англ.- 3-е изд.- СПб.: БХВ-Петербург, 2006-688 с.

5. Павловская Т. А., Щупак Ю. А. C++. Объектно-ориентированное программирование: Практикум. — СПб.:Питер, 2006. — 265

6. Н.Б.Культин. Программирование в Turbo Pascal 7.0 Delphi. – СПб.: БХВ – Санкт-Петербург, 1999

1. В. В. Подбельский. Язык Си++. М.: Финансы и статистика, 2000, с. 4, 20-23.

Write a comment