КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Класи і об'єкти, члени класів
Класи і об'єкти, члени класів: атрибути і (класні) функції (function member), конструктори, деструктори, позакласні функції, утиліти класів; область видимості і права доступу: відкриті і закриті члени, порушення прав доступу, дружні функції
Побудована досі інкапсуляція має серйозний недолік, який ілюструється наступним кодом:
employee1.name[3]=’о’; employee1.name[7]=’в’; employee1.name[8]=’а’; employee1.name[9]=’\0’;
Поки що структури зовсім прозорі. Вони інкапсулюють свої члени, але дозволяють доступ до них усім, хто бачить самі структури. Використання, а ще гірше, несанкціонована зміна атрибутів залишаються неконтрольованими. В найбільш послідовному виді проблема регулювання доступу до інкапсульованих членів розв'язується в концепції класу. Як і структура, клас складається із членів: атрибутів і методів. Він ділиться на відкриту (public) і закриту (private) частини. Головна відмінність від структур полягає у правилі замовчування. Відкриту частину класу визначають явно. Все, що не позначено відкритим, закрите. Структури звичайно відкриті, хоча можливе визначення закритої частини структури. Цим користуються рідко.
class Point { // Атрибути private: double _x; double _y; // Методи public: Point (double a=0, double b=0); Point operator+(Point); bool operator==(Point); double modulus (); double phi (); }
Одночасно ми доповнили визначення точки методом її ініціалізації. Створення об'єкту — екземпляру класу (class instance) тепер інкапсулюється у окрему програмну одиницю — метод, названий конструктором.
Point:: Point(double a, double b) {_x=a; _y=b;}
Визначення Point тепер необхідно доповнити оголошенням конструктора (зверніть увагу на замовчувані значення його параметрів). Екземпляри точок створюються
тепер у такий спосіб
Point u(); //u._x == 0, u._y == 0 Point v(1.0, 2.0); Point *p = new Point(1.0, -1.0);
Те ж саме стосується класу Employee
const short kMaxNameSize = 20; class Employee { // Атрибути private: char name [kMaxNameSize]; unsigned int id; float salary; // Методи public: Employee(char*, unsigned int, float); void printEmployee(); }
Тепер доступ зовні до атрибутів закрито, а ось реалізація конструктора
Employee::Employee(char *n, unsigned int i, float s) { strncpy(name, n, kMaxNameSize); name[kMaxNameSize-1]='\0'; id = i; salary = s; cout << "Зареєстровано службовця” << "\n"; printEmployee(); }
Метод, симетричний конструктору, називається деструктором. Навіть якщо у визначенні класу конструктори і деструктори відсутні (це можливо), система програмування наділяє клас замовчуваними порожніми конструктором і деструктором. Тому конструктор і деструктор є завжди. Щоб проблему стало краще видно, розглянемо трохи складніший варіант структури EmployeeP, в якому передбачено виділення динамічної пам'яті
class EmployeeP { // Атрибути private: char * name; unsigned int id; float salary; // Методи public: EmployeeP(char*, unsigned int, float); ~EmployeeP(); void printEmployee(); };
Тепер пам'ять для прізвища виділяється в конструкторі
EmployeeP::EmployeeP(char *n, unsigned int i, float s) { name = new char[strlen(n)+1]; strcpy(name, n); id = i; salary = s; };
Знову розглянемо два визначення
EmployeeP employee1("Клічко", 1, 300.0); EmployeeP *employee2; employee2 = new EmployeeP("Ребров", 2, 200.0);
Об'єкт employee1 автоматичний, він знищується компілятором автоматично при виході з блоку. Об'єкт employee2 динамічний, його необхідно знищувати явно інструкцією delete. Але в обох випадках рядок name продовжує зберігатися в пам'яті, власне як сміття. Звільнити її задача деструктора, який викликається неявно в момент закінчення життя відповідного об'єкта.
EmployeeP::~EmployeeP() { delete [] name; }
Поява класів не ліквідує звичайних функцій, не інкапсульованих у класах. Образно їх можна називати позакласними функціями (non-member function).
Ось приклад функції для обчислення скалярного добутку векторів, заданих своїми кінцевими точками:
double prod (Point u, Point v) { return u.modulus()*v.modulus()*cos(u.phi()-v.phi()); }
Позакласні функції, параметрами яких служать екземпляри певних класів, називають утилітами цих класів. Одні класи можуть використовуватися у визначенні інших, наприклад, клас трикутників використовує (агрегує) клас точок
class Triangle { Point a, b, c; public: Triangle (Point, Point, Point); perimeter(); square(); }
Звичайно для атрибутів у класах передбачають методи доступу — парні функції запису і читання атрибуту. Скажімо для класу точок це були б функції
void Point:: setX (double); double Point:: readX();
void Point:: setY (double); double Point:: readY();
або double& Point:: X (double); double& Point:: Y (double);
Загальнодоступність методів доступу корисна не завжди. Скажімо візьмемо задачу нарахування зарплати. Відомість про зарплату кожного співробітника є конфіденційною інформацією, а тому в клас не доцільно мати відкритий метод читання окладу, не кажучи вже про метод встановлення окладу. Припустимо, що за нарахування запрали відповідатиме метод printCheck класу Payroll
void Payroll:: printCheck(EmployeeP *payee) { cout << "Pay $" << payee->getSalary() << " to the order of " << payee->getName() << "...\n\n"; }
Порушення прав доступу відбувається шляхом визначення дружнього класу або дружньої функції. Нею може бути метод іншого класу або навіть позакласна функція, звичайно, утиліта класу. У випадку дружньої функції необхідне оголошення типів параметрів, для того щоб серед багатьох визначень функції з указаним іменем вибрати потрібну. Дружній об'єкт одержує винятковий доступ до закритої частини класу. Друзів обирають обережно!
class EmployeeP { friend void Payroll:: printCheck(EmployeeP *); // також: friend class Employer; // крім них: friend bool operator> (const EmployeeP&, const EmployeeP&); // Атрибути private: char * name; unsigned int id; float salary; // Методи EmployeeP(char*, unsigned int, float); float getSalary(); public: ~EmployeeP(); void printEmployee(); char* getName(); };
Ось операція порівняння службовців за окладами
bool operator>(const EmployeeP& e1, const EmployeeP& e2) { return e1.getSalary()>e2.getSalary(); }
Доповніть клас EmployeeP атрибутом, що зберігатиме дату зарахування на роботу і визначіть порівняння службовців за стажем роботи.
Закритим може виявитися навіть конструктор. Можна домовитися, що створення службовця є операцією, що вимагає особливих повноважень. Ось чому ми перенесли конструктор у закриту частину та призначили дружнім класом клас Employer — службовця на роботу приймає роботодавець
EmployeeP * Employer:: createEmployee(char *n, unsigned int i, float s) { EmployeeP *anEmployee; anEmployee = new EmployeeP (n, i, s); return anEmployee; }
Тепер ми проаналізували поняття абстракції, інкапсуляції, включаючи концепцію приховання інформації (information hiding). Клас Employee служить програмною абстракцією службовця, клас Employer — працедавця, клас Payroll — відділу зарплати. Кожен клас інкапсулює в собі дані і методи, приховуючи реалізацію методів і зображення даних. Частина даних видима або доступна через відповідні методи доступу всім, інша — лише привілейованим об'єктам.
5.2 Інтерфейс класу, реалізація класу; визначення і оголошення класу Інтерфейс класу, реалізація класу; визначення і оголошення класу
Традиційно інтерфейс і реалізацію класу розміщують у різних файлах, як це було з оголошеннями і визначеннями об'єктів. Інтерфейс записують у заголовні файли. Ось приклад заголовного файлу для класу точок
//Point.h #ifndef POINT_H #define POINT_H class Point { // Атрибути private: double _x; double _y; // Методи public: Point (double a=0, double b=0); Point operator+(Point); bool operator==(Point); double modulus (); double phi (); } #endif
Ось файл реалізації
//Point.cpp # include ”Point.h” #include <cmath> Point:: Point(double a, double b) { _x=a; _y=b; } Point Point::operator+(Point v) { Point w; w._x = _x + v._x; w._y = _y + v._y; return w; } bool operator== (Point v) { return (_x == v._x) && (_y == v._y); } double modulus () { return sgrt(_x*_x+_y*_y); } double Point::phi () { return atan(_y/_x); } //Кінець Point.cpp
Буває зовсім коротке оголошення класу, наприклад,
//Payroll.h #ifndef PAYROLL_H @define PAYROLL_H class EmployeeP; class Payroll { ………………………………… void printCheck(EmployeeP *) } #endif
Без оголошення класу EmployeeP компілятор не зміг би відкомпілювати оголошення функції printCheck, де EmployeeP використовується як тип параметру. Інтерфейс класу EmployeeP знадобиться лише у файлі Payroll.cpp, де буде визначатися
реалізація. Туди і помістимо #include
//Payroll.cpp #include ”Payroll.h” #include ”EmployeeP.h’
З цієї причини в заголовному файлі не розміщують визначень, які можуть потребувати включення додаткових файлів.
Дата добавления: 2014-01-04; Просмотров: 390; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |