Конструктори та деструктори при наслідуванні

Визначення об'єкту

Об'єктом будем називати змінну типу "class". Об'єкт оголошується в головній функції main після створення класу. Об'єкт це конкретний екземпляр абстрактного класу.

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

 

Доступ до членів класу

Після визначення реального об'єкту класу постає необхідність отримати доступ до членів цього об'єкту. Для цього використовується оператор "." , який дозволяє звернутись до елементів об'єкту безпосередньо. Цей оператор застосовується до об'єктів, що розміщені в статичній пам'яті.

 

void main()

{

int a;

Dog F;

F.Vaga = 10;

F.Sleep();

Dog G;

G.Vaga = 15;

G.Sleep(); //вага об'єктів G i F є абсолютно різнною

}

 

Значення присвоюється змінним а не типу. Тому запис Dog.Vaga = 5 є недопустимим.

 

Типи доступу до членів класу

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

Відкриті члени класу оголошуються як public і вони доступні всім функціям програми

щоб перший приклад став коректний, необхідно змінну Vaga оголосити як public.

 

Відкриті методи доступу

Щоб передавати та повертати значення закритих змінних необхідно створити відкриті функції класу, відомі як методи доступу.Відкритий метод доступу - це функція класу, яка працює з закритими змінними класу, і дозволяє приховати від користувача подробиці зберігання даних в об'єктах. Таке застосування дозволяє модернізувати способи зберігання та обробки даних всередині класу не переписуючи при цьому методи доступу та їх виклики в зовнішньому програмному коді. Методи доступу поділяються на ті що встановлюють значення змінних, їх ім'я починається зі слова Set і далі вказується ім'я змінної з якою працює метод. Параметром є число, що буде присвоєно даній змінній. Функція Set нічого не повертає, тому тип результату буде void. Другим методом доступу є функція, що повертає значення встановленої змінної. Ім'я цього методу починається зі слова Get та назви змінної з якою працює функція. Функція не приймає параметрів, а тільки повертає значення встановленої закритої змінної. Методи доступу оголошуються в межах класу. Вони викликаються через оголошений об'єкт класу.

 

class Dog

{

public:

void SetVaga(int V);

int GetVaga();

void Sleep();

private:

int Vaga;

}

 

void main()

{

Dog F;

F.SetVaga(10);

int a=F.GetVaga();

F.Sleep();

}

 

Реалізація функцій класу

Кожна функція класу, в тому числі і відкриті методи доступу мають бути реалізовані. Тобто мати розписаний набір операторів, які описують дії функції. Реалізація функцій класу починається з типу результату функції, потім записується ім'я класу, в якому оголошена дана функція, потім оператор "::" (подвійна двокрапка), далі ім'я функції та список її параметрів. В межах фігурних дужок розписується тіло функції.

 

void Dog::SetVaga(int V)

{

Vaga=V;

}

int Dog::GetVaga()

{

return Vaga;

}

void Dog::Sleep()

{cout<<"Dog is sleeping";}

 

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

Лекція 4

Вбудована реалізація функції

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

 

class Cat

{

public:

int Get Weight(){return ;W;}

void SetWeigth(int aW);

private:

int ;W;

};

 

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

 

inline void Cat::SetWeight(int aW)

{iW = aW};

 

Конструктори та деструктори

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

 

class Cat

{

public:

Cat(){}; //конструктор

Cat(a,b){A=a; B=b;} //конструктор з парметрами

private:

itn A,B;

}

 

void main()

{

Cat F1; //створення за конструктором без параметрів

Cat F2(5,4); /*створенян за конструктором з параметрами, при //цьому відбуваєтьяс ініціалізація змінних класу //змінна А прийме значення 5, а B=4

}

 

Деструктор - це функцію класу, яка видаляє з пам'яті відпрацьовані об'єкти та звільнює виділену для них пам'ять. Деструктор має ім'я класу зі знаком

~Cat();

Деструктори нічого не приймають і нічого не повертає. Якщо не оголошувати в класі конструктор та деструктор, компілятор зробить це сам автоматично. Коструктори та деструктори, які нічого не приймають та нічого не виконують називаються стандартними.Всі об'єкти в програмі мають бути спочатку створені а потім знищені. Якщо додатково не проводити знищення об'єкта компілятор зробить це сам в кінці програми.

 

Постійні фунції класу

Для оголошення фунції постійною використвуєтья ключове слово const. Постійна функція не може змінити значення жодного члену класу. Таке оголошення дозволяє компілятору знаходити помилки до того як вони стануть причиною пробеми. Наприкад фунція відкитого доступу Get ніколи нічого не змінює, а тільки повертає результат фунції Set, тому при оголошенін функції Get необхідно записати її як постійну.

 

Файл заголовку

Це файл з розширенням h.pp в якому розміщується оголошення класу з переліком змінних і функцій. Реалізація фунції записуєтья в окремому файлі з розширенням c.pp . Імена цих файлів зазвичай таке саме як ім'я основного проекту. Файл заголовку необхідно включити у файл основного коду. Для цього використовується директива

#include "Cat.hpp"

Такий поділ по файлах дозволяє застосувати файл заголовку в декількох файлах програми. Це зменшує кількість створених класів.

 

Структури

Мова С++ пішла від мови С, яка включала в себе структури. Ці структури не включали функцій, а тільки значення і поля. Структура в якій всі елементи по замовчуванню є public і яка містить функції використвоується в мові С++. Якщо в такій структурі поміняти ключове слово struct на class ніяких змін не відбудеться, компілятор помилки не видасть.

 

ГЛОБАЛЬНІ ФУНКЦІЇ

Кожна програма на С++ містить хоча би одну фунціїю main(), яка при завантаженні програми викликається автоматично. Ця функція може викликати інші функції, які не є членами класу. До них можна звернутись з будь-якого місця програми. Вони називаютьяс глобальними. Глобальні фунції, як і будь-які інші фунціх мають бути описані раніше за виклик. Виклик глобальних фунцій відбувається за ім'ям. Всі фунції класифікують на два види:

- користувальні (створюються програмістом під час написання конкретної програми)

- вбудовані (належать до пакету компліятора)

 

Фактичні значення що передаються у фунцію при її виклику називаються аргументи. Вони мають відповідати списку параметрів, що створюється при оголошенні фунції(кількість і тип). Оголошення фунції разом зі списком параметрів називається прототипом фунції, він включає: тип результату фунції, ім'я фунції, та список параметрів(тип параметра та його ім'я). Існує 3 способи оголошення функції:

-записати прототип фунції у файл, а потім використати директиву include

- записати прототип у той файл, де фунція викликається

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

 

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

 

int f(int A, int B){int c,d; A=A+c; B=B+b; d=c+d; return d;}

void main()

{

int a,b; a=10; b=15;

c=f(a;b)

}

 

Перевантаження функції

Мова С++ дозволяє створювати декілька фунцій з однаковим іменем. Такі функції називають перевантаженими, і вони обов'язково мають відрізнятись між собою списками параметрів(типом або кількістю параметрів або тим і тим одночасно)

int F(int);

int F(int, float);

int F(float, float, float);

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

 

Перевантаження унарних операцій

@x - унарні операції

1) x.operator@() - нестатична функція класу без параметрів

2) operator(x) - функція що не є членом класу і має один аргумент, який може бути змінною чи посиланням на змінну певного класу.

 

Перевантаження бінарних операцій

x@y - бінарні операції

1) x.operator@(y)

2) operator@(x,y)

 

Конструктор копій

КК (конструктор копій) створює копію існуючого об'єкту. При передачі у функцію або повернення у вигляді значення, створюється temp-копія.

Всі КК приймають оди параметр - посилання на об'єкт свого класу. Таке посилання const, оскільки конструктор не змінює об'єкту.

 

class Cat

{

Cat(){}

Cat(Cat & theCat);

}

 

Стандартні КК копіюють кожну змінну об'єкта в змінну новоствореного об'єкту. Таке copy - поверхневе. Воно підходить для будь-яких статично розміщених даних. Якщо змінні є вказівником, то стандартний КК не спрацює. Треба створити власний. Власний КК дозволяє провести глибоке копіювання і задіяти додаткові комірки.

 

Cat(const Cat &);

~Cat()

int GetAge() const {return *pAge}

private:

int *pAge;};

Cat::Cat()

{

pAge = new int;

*pAge = S;

}

Cat::Cat(const Cat&Kopia)

{

pAge = new int;

*pAge = Kopia.GetAge();

}

Cat::~Cat()

{

delete pAge;

pAge = 0;

}

void main()

{

Cat Frisky;

cout<<Frisky.GetAge();

Frisky.SetAge(6);

Cat Boots(Frisky);

}

 

Передається у функцію у вигляді параметра, як значення, то відбувається звернення до КК компілятора. Коли об'єкти класу Cat вилізуть з області дії, будуть викликати ті деструктори, які очистять вказівники і в цілях везніх присвоять значення 0.

 

Перевизначення присвоєння

Суть зводиться до представлення операції в явному вигляді. Перевизначення ОП не наслідує її дія відповідає дії КК.

void main()

{

Cat Frisky;

Cat Whiskers;

Whiskers = Frisky;

Cat Cat::operator = (Cat &H)

{

*pAge = H.GetW();

return *this;

};

}

Перевизначення додавання для класу комплексних чисел

class Complex

{

public:

Complex(float r, float i){real=r; float=I;}

Complex operator + (Complex(2)){return Complex(real+C2.real, imag+C2.imag);}

private:

float real, imag;

}

 

Перевизначення операції множення простих дробів

class Drib

{

public:

int Ch, Zn;

Drib(int c, int z){Ch=c;Zn=z;}

Drib operator + (Drib S1)

{

Drib R;

R.Ch = Ch*S1.Ch;

R.Zn = Zn*S1.Zn;

}

}

 

S=P.operator + (E); <=> S=P+E;

 

Дружні функції

Нехай оголошено 2 класи: вектори і матриці. Всі їх члени закриті. Необхідно створити функцію, яка множить матрицю на вектор. Якщо створити функцію, як глобальну, то це знизить ефективність, оскільки при кожному виклику необхідно буде перевірити індекси як вектора так і матриці. Логічно оголосити її як член класу вектор та член класу матриці. Тоді перевіряти індекси не потрібно. Але та сама функція не може бути членом двох класів.

В С++ ця проблеми вирішується через оголошення функції, дружньої класу, або обом класам. Така функція має доступ до всіх членів класу(в тому числі private)

 

Модуль 2:

Наслідування класів

Це один з принципів ООП, який дозволяє узагальнені змінні та функції виносити в окремий клас, який називається базовий клас. Для різних предметів реального світу необхідно визначити спільні ознаки та риси поведінки і описати їх в базовому класі. Клас, який доповнює функцій ні можливості базового класу називається похідним класом. Вони є розширенням базового класу, який може мати більш ніж один похідний клас(базовий клас: ссавці, похідні класи: собака, кіт, кінь, слонопотам). Наслідування ОП дозволяє не повторювати спільних ознак і функцій в кожному новому класі. Похідні класи надають можливість формування простого, гнучкого та ефективного механізму для опису альтернативних інтерфейсів класу без їх перепрограмування та перекомпіляції. Використовуючи похідні класи можна отримати спільний інтерфейс для декількох різних класів так, що можна працювати з їх об'єктами однаковим чином з різних частин програми. Як правило такий інтерфейс містить інформацію про тип. При оголошенні наслідування базовий клас має передувати похідному, похідний клас оголошується так:

class ClassName: BaseClassName Type;

Якщо базовий клас не був оголошений, компілятор повідомить про помилку. Об'єкти похідного класу мають всі атрибути базового классу та свої власні.

class Mammal

{

public:

Mammal();

~Mammal();

void Speak();

void Sleep();

protected:

int Age;

float Weight;

};

class Dog : public Mammal

{

public:

Dog();

~Dog();

void WagTail();

private:

int H;

};

 

void main()

{

Dog Fido; // Fido може говорити, спати й махати хвостом

};

 

Типи наслідування

 

1)

class Dog : public Mammal

{

private:

public:

protected:

};

 

2)

class Dog : private Mammal

{

private:

public:

protected:

};

 

3)

class Dog : protected Mammal

{

private:

public:

protected:

};

 

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

Publicдозволяє використовувати pubic-елементам головного класу, функціям і елементам головного класу(в головному класі вони стають відкритими)

Privateдозволяє використовувати public-елементи базового класу тільки функціям похідного класу (в похідному класі вони стають private)

Protectedдозволяє використовувати відкриті та захищені елементи базового класу функціям похідного (в похідному класі вони стають protected)

 

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

 

Конструктори та деструктори при наслідуванні

Об'єкти класу Dog одночасно є об'єктами класу Mammal, коли створюється об'єкт Fido похідного класу, то за замовчуванням спочатку викликається конструктор класу Mammal а потім конструктор класу Dog. При видаленні об'єкту Fido спочатку викликається деструктор похідного класу, а потім деструктор базового класу. Кожний деструктор знищує відповідну частину.