Лекція 4
Область дії об’єкта. Правила доступу до компонентів класу
Кожен об'єкт класу має власну копію всіх даних-членів класу. Бувають випадки, коли одна копія змінної повинна спільно використовуватися всіма об'єктами класу. Тоді застосовується статична змінна класу. Очевидно, що статичні члени класу існують тоді коли не існує ніяких об'єктів цього класу. Оголошення статичних членів класу починається з ключового слова static. Статичні дані-члени мають область дії клас і повинні ініціалізуватися один раз в області дії файлу. Доступ до відкритих статичних членів класу можливий за допомогою будь-якого об'єкта класу або за допомогою імені класу і бінарної операції дозволу області дії. Для доступу до відкритого статичного члена, перед даним-членом ставиться позначення класу і потім бінарна операція дозволу області дії (C++), або позначення класу і символу крапка (С#). Наприклад, у мові C++:
class Empl
{
public:
setCount(int);
static int getCount();
private:
static int count;
};
int main() {
Empl *pEmpl;
pEmpl = new Empl;
pEmpl > setCount(10);
delete pEmpl; count = getCountC);
}
Приклад у мові C#:
class Empl {
public setCount(int)
{…};
public static int getCountQ {…};
private static int count = 10;
int main()
}
Empl empl;
mpl - new Empl();
empl.setCount(10);
int count = Empl.getCountQ;
Програмні конструкції, які можуть використовувати методи і значення об'єкта, створеного на основі класу, називаються компонентами класу і діляться на три групи:
- ресурси похідного класу;
- «друзі» класу - підпрограми і класи, оголошені спеціальним
чином;
- звичайні підпрограми - не «є», ні ресурсами класу, ні «друзями» класу.
Для обмеження доступу до компонентів базового класу і забез^ печення ефективної організації взаємодії об'єктів, які створюються на основі класів, доступ до компонентів базового класу, і отже, до компонентів об'єкта, створеного на основі цього класу, регулюється за допомогою спеціальних правил. Ці правила визначають область видимості членів класу і тим самим область дії. Правила застосовуються до частин класу. У зв'язку з цим у тілі класу зазвичай передбачається три такі частини:
- закрита (private);
- захищена (protected);
- відкрита (public).
За умовчанням всі члени класу є закритими (private). Визначаються такі правила доступу до вказаних частин класу
(рис 1.7):
- частини, оголошені, як закриті, доступні методам даного класу і друзям класу;
- частини, оголошені, як захищені, доступні методам даного класу, методам похідного класу і друзям класу;
- частини, оголошені, як відкриті, доступні будь-яким конструкціям програми.
Рис. 1.7. Схема взаємодії конструкцій
Передбачається, що члени, представлені у відкритій частині, показують доступні ресурси класу (інтерфейс). Члени закритої частини і реалізація членів відкритих частин недоступні клієнтам класу і представляють реалізацію ресурсів класу.
Проте методи цього класу і «друзі» можуть мати доступ до членів закритих частин класу, а опис даних закритої частини видно користувачеві класу. Цей недолік організації класів порушує принцип приховування інформації. Наприклад:
#include «timel.h»
int main ()
{
Time t;
// Помилка: Time: :hour' недоступно
t.hour = 7:
}
Наприклад class Airplanes
{
private:
Airplane_Data Database [100];
class AN 140 : Airplanes
void process_AN140(int);
void AN 140 :: process_AN140(int i)
Airplane_Data MyData = Database [і]; //помилка досту-
пу
};
}
До того ж, якщо оголошений об'єкт MyAN140 класу AN 140, то він міститиме пам'ять для даних Database члена базового класу Airplanes. Якби Database був членом захищеної частини, то до нього можна було б звертатися з похідного класу. Однак надійніше, якщо похідний клас маніпулює успадкованими компонентами, використовуючи загальні або захищені частини.
Частини класу можуть бути розташовані у будь-якому порядку, проте їх рекомендується розміщувати так: спочатку відкриті, потім захищені і, нарешті закриті.
Доступ до private-частини класу. «Дружні» підпрограми
У мові C++ передбачається обхід механізму приховування ін формації шляхом застосування спеціальних підпрограм, які нази ваються «дружніми» функціями (підпрограмами). У класі підпрог рами описуються прототипами з ключовим словом friend, а визна чаються поза класом, до ресурсів яких вони мають доступ. Наприклад: class WithFrend і
friend void setX(WithFriend &, int); public:
private;
int x;
void setX(WithFriend &c, int val)
/
I
c.x ~. val;
main () І
WithFriend obj; setX(obj, 10);
З прикладу бачимо, що зв'язок «дружньої» підпрограми з prívate частиною класу встановлюється через позначення об'єкта як фактичний параметр, а оператор дозволу видимості (::) у цьому випадку не використовується.
Окрімттідпрограм «друзями» класу можуть бути інші класи, що мають такий же доступ до private-частини, як і «дружні» функції: class One
{
public:
friend class Two;
Механізм «дружності» не має властивостей симетричності і транзитивності.
Оператор привласнення для об'єктів
У мовах C++ і С# передбачається форма оператора привласнення, яка може використовуватися для привласнення значень одного об'єкта іншому. Обидва об'єкти мають належати до одного
класу.
Наприклад, у мові С#:
class AssStat {
public:
private:
main
t
int nVal; char с Val;
A ssStat obj 1 ; AssStat obj2;
оЬ]2 = оЬ)1;
і
У мові С++ наведена форма оператора привласнення називається побітовим копіюванням за умовчанням.
Реалізується привласнення побітовим копіюванням, тому, якщо в класі використовується динамічний розподіл пам'яті, то правильне виконання оператора привласнення не гарантується.
Спеціальні методи класу
Значення даних-членів класу можна встановити за допомогою методів класу після створення об'єкта. Також можна виконати потрібні для знищення об'єкта дії. Проте в класах передбачаються опис і використання двох типів спеціальних методів, які називаються «конструктори» і «деструктори», які частково виконують вказані дії.
Конструктори - спеціальні методи, реалізуються як закриті підпрограми, створюють екземпляри класу. Суть створення об'єкта полягає в тому, щоб захопити пам'ять для об'єкта (як правило, разом) та ініціалізувати початковими значеннями компоненти-даних об'єкта, що описані в класі, на основі якого створюється об'єкт. Таким чином, коли необхідно створити об'єкт, викликається конструктор класу.
У мовах програмування С++ і С# існують такі правила для оголошення конструкторів:
- позначення конструктора мас бути таким же, як і позначення класу;
~ конструктор може мати список параметрів.
Розрізняють три типи конструкторів - за умовчанням, копіювання, додатковий.
Конструктор за умовчанням. У мові C++ він називається void-конструктор та реалізується за допомогою закритої підпрограми без параметрів або із списком параметрів, призначених за умовчанням.
Конструктор копіювання. У мові C++ він називається сору-конструкгор та реалізується за допомогою закритої підпрограми з одним параметром. Параметр є посиланням, що типізований класом. До того ж, значення компонентів-даних створюваного об'єкта копіюються з уже створеного об'єкта класу вказаного типу в новостворюваний. Копіювання, яке здійснює «штатний» конструктор-копіювання, називається поверхневим і засноване на операторі привласнення. Суть його роботи полягає в такому: якщо дані-компоненти об'єкта є вказівними змінними та ініційовані посиланнями на динамічну пам'ять в групі, а копіювання здійснилося, то коли один об'єкт знищується — в результаті звільняється пам'ять, зайнята значеннями, але другий об'єкт (копія) вказуватиме на звільнену ділянку пам'яті, що звичайно неприпустимо. Щоб уникнути цього застосовується глибоке копіювання, коли значення, розміщені в динамічній пам'яті в купі, переносяться на нові ділянки пам'яті об'єкта, який створюється.
Наприклад, у мові C++: class А {
public:
А(); А (А&);
int getIntA();
private:
int * iintA; int * intB;
i.
Конструктор з глибоким копіюванням:
А :: А(А & г) {
intA - new int; intB = new int; *intA Ш r.getlntAQ; ftntB = * (r.intB);
Наприклад, у мові C#:
public A() {
InitO;
public A(A r)
itsA = r.getItsA(): itsB = r.itsB;
і
public int getftsAQ
return itsA;
private int itsA; private int itsB; private void Init()
itsA= 1; itsB = 2:
Додатковий конструктор - забезпечує список будь-яких формальних параметрів, за допомогою якого компоненти-дані об'єкта ініціалізувалися значеннями фактичних параметрів.
Реалізуючи конструктори класу, використовуємо механізм перевантаження підпрограм, тому всі конструктори класу мають однакове позначення, але різні реалізації.
Якщо в класі не оголошений конструктор, то він створюється, як правило, автоматично у формі конструктора за умовчанням для цього класу.
Всі конструктори реалізуються у формі процедур^__
Деструктори - це спеціальні методи класу, які ліквідують екземпляри класу (об'єкти), коли настає кінець періоду їх існування. Суть ліквідації полягає у звільненні пам'яті зайнятою під об'єкт конструктором у групі.
У мовах C++ і С# для деструкторів передбачені такі правила:
- позначення деструктора повинне збігатися з позначенням класу і перед ним має стояти символ «~»;
- деструктор у класі передбачається тільки один;
- деструктор реалізується у формі процедури без параметрів;
- якщо у класі не оголошений деструктор, то він створюється автоматично;
- якщо настав кінець області існування об'єкта і деструктор не викликаний вручну, то він викликається автоматично.
Окрім звільнення пам'яті, деструктор виконує всі дії, протилежні діям відповідного конструктора. Наприклад, у мові C++:
class myclass();
{ public:
mvclass()
{
m_nl - 0;
myclass (myclass &(obj)) {
m_nl = obj.mnl;
void set mn!(int NewVal)
\
m_nl = NewVal;
int getmnl(); t
return m nl;
~myclass(); private:
intm nl
mam()
}
int nVal;
my class objl; // оголошення об'єкта класу my class
obj.set m_nl(10);
nVal = getm_nl();
my class obj2;
my class (obj 1);
nVal = obj2. get m_nl();
