Первомайський ЦНТТУМ
Гурток «Сучасні технології програмування»
Керівник гуртка Семенова Олена Анатоліївна
Розробка заняття на тему:
Тема: «Класи ООП. Конструктори. Поліморфізм.»
Обладнання: ПК, ПЗ середовище програмування DevC++.
Хід заняття
1. Організаційний момент та розминка.
1. Для початку, розминка, як завжди, для мозку і для рук :) : складаємо 12 слів, що вміщують не менше 6 символів із літерами : «ф, і, в, а, п, р, о, л, д, ж, к, е, н, г, т, ь, и, м, ш, у, с, б».
2. Записуємо 3 рядка цих слів за допомогою сліпого десятипальцевого методу набору через кому.
3. Перевіряємо правильність набору та ставимо усні оцінки своїй роботі. Пам“ятаємо, що ми чесні :).
2. Основна частина. Повторення та перехід до розгляду нової теми.
Сьогодні нас чекає продовження вивчення класів, їх методів та реалізації основних принципів ООП в класах. Але для початку, давайте пригадаємо дещо:
1. Які основні принципи ООП ви пам“ятаєте?
2. Як ви розумієте поняття інкапсуляція, поліморфізм, наслідування?
3. Що називають методом класу?
4. Для чого потрібні модифікатори доступу в класах?
Отже, методом класу є функція, яка описує дії об“єкта класу, доступ до властивостей об“єкта здійснюється за допомогою модифікаторів, так званих, «три «р»», через які реалізується принцип інкапсуляції в ООП. Реалізацію цього принципа через модифікатор доступу???? , правильно, private, ми розглянули на минулому занятті. Сьогодні черга наступного принципу — поліморфізм. Ми вже торкались цього поняття, коли знайомились з поняттям ООП взагалі. Пам“ятаєте походження цього терміну? Так, від давньогрецького πολύμορφος —різноманітний.
Ще ми згадали, що в ООП поліморфізм — це можливість обʼєкта вести себе по-різному в залежності від ситуації та реагувати специфічним для цього класу обʼєктів чином.
Пригадаймо клас кавомолок, який ми розглядали. Як проявляється принцип поліморфізму в даному класі? В способах, за якими працюють кавомолки: механічний або електричний.
Ці способи, власне, називаються методами класу - функції обʼєкту класа, за допомогою яких ви можете взаємодіяти з обʼєктом. Створюючи свій клас ми надаємо об“єктам певні властивості, які називають полями і створюємо функції — методи, що описують роботу об“єктів класу.
Але існують і спеціальні, особливі методи. Одним з таких є конструктор класу. Як і усі терміни язика програмування, від англійського «constructor», але саме слово латинського походження «constructor»- будівник. Вам усім з дитинства відоме це слово :). Тобто, за аналогією, в класах ООП -конструктор – це спеціальний блок інструкций. В чому його особливість?
Давайте для початку розглянемо невеличкий приклад на нашому «кошачому» класі: в нас є властивості класу: ім“я та вік, та паблік методи: геттери, сеттери та ще один, який виводить в консоль значення властивостей об“єкта. Якщо ми створимо об“єкт і для цього об“єкта викличемо метод Print. Що ми побачимо в консолі?
class Cats //Об“являємо та описуємо клас котів
{private:
string name;
int age;
public:
string GetName ()
{return name;}
int GetAge ()
{return age;}
void SetName (string sName)
{name=sName;}
void SetAge (string sAge)
{name=sAge;}
void Print ()
{ cout<<“cat1 name is - \t“<<name<<“\tcat1 age is - \t“<<age<<endl;}
};
іnt main ()
{
Cats cat1;
cat1.Print();
return 0;}
Так, ми це вже бачили і раніше, не тільки на прикладі полів класу, та знаємо, що отримаємо, м“яко кажучи, сміття, замість зрозумілих та коректних даних. Чому?
Тому що значення властивостей у нас неініціалізовані і компілятор видає ті рандомні значення, які знайшов у тому осередку пам“яті, де помістився наш об“єкт.
Щоб такого сміття уникнути, ми застосовували сеттери для полів:
cat1.SetName (“Simone“);
cat1.SetAge (2);
Тепер при компіляції в консолі у нас виведуться зрозумілі та визначені дані, оскільки поля класу ініціалізовані.
Але такий спосіб не дуже зручний. Уявіть собі, что полів значно більше … До того ж може виникнути ситуація, коли нам за змістом програми треба буде, щоб такі об“єкти, з невизначеними, рандомними сміттєвими значеннями полів, взагалі не мали можливості з“явитись.
Для вирішення таких проблем і існують конструктори. Як я вже казала, конструктор — це особлива функція, тобто, метод класу. В чому ж її особливість. Назву лише основні три: по-перше, для конструктора не визначається тип(void, int та ін.), тому що він не повертає ніяких значень, як функції; по-друге, його назва завжди така, як назва класу; і, нарешті, конструктор в класі є завжди, незалежно від того, написали ви його чи ні. Конструктор класу створюється компілятором автоматично при створенні об“єкта класу, навіть якщо його не видно. Він просто додається і існує, не виконуючи ніяких дій. Такі конструктори називаються «конструктори за замовчуванням».
Виглядає конструктор для нашого класу таким чином:
Cats ()
{
};
І якщо ми його отак запишемо, це буде як раз такий самий варіант, як конструктор «за замовчуванням» і компілятор не буде створювати інший. Він бачить, що конструктор в класі створено і не має ніякої різниці, що в нього всередині і як він працює.
Але в нашому класі є поля, які нам треба ініціалізувати для початку і тому в нашому конструкторі, оскільки це все ж таки функція, мають з“явитись визначені параметри.
Cats (string c_name, int c_age)
{
};
Синтаксис конструктора: Назва - Cats, параметри в дужках відповідають полям класу, з найменуванням змінних таким чином, щоб ви розуміли, які значення і куди ми передали - string c_name, int c_age, фігурні дужки, що обмежують зміст конструктора та, звичайно, крапка з комою в кінці.
Тепер нам треба надати значення полям, які в нас є в класі. Це робиться аналогічно тому, як ми робили це, коли писали сеттери для класу.
class Cats //Об“являємо та описуємо клас котів
{private:
string name;
int age;
public:
string GetName ()
{return name;}
int GetAge ()
{return age;}
Cats (string c_name, int c_age)// Синтаксис конструктора: Назва, параметри, зміст в фігурних дужках.
{name=c_name;
age=c_age;
};
void SetName (string sName)
{name=sName;}
void SetAge (string sAge)
{name=sAge;}
void Print ()
{ cout<<“cat1 name is - \t“<<name<<“\tcat1 age is - \t“<<age<<endl;}
};
іnt main ()
{
Cats cat1;
cat1.Print();
return 0;}
Якщо ми залишимо наш код таким, як він є, то… ? Так, я не дарма задаю таке запитання :) .. Звичайно, тому що повинні вилізти якісь «граблі», адже ми трошки змінили зміст опису класу. На що свариться компілятор? Що при створенні об“єкта класу очікується надання значень полям класу.
Чому так? Тому що ми створили конструктор і компілятор бачить, що конструктор в класі є, тому при створенні об“єкта класу конструктор за замовчуванням не створюється.
Давайте задовольнимо наш компілятор, щоб не сварився :) та надамо значення полям нашого першого кота :). Які саме, підкажіть...
Cats cat1(«Simone», 3);
Зробим такі зміни в створенні об“єкту в програмі:
іnt main ()
{
Cats cat1(«Simone», 3);
cat1.Print();
return 0;}
Тепер компілятору все подобається. І ви бачите, що завдяки такому конструктору можна значно спростити і код і полегшити роботу програміста :), оскільки не треба кожен раз писати сеттери для кожного поля.
Створіть за аналоією ще один об“єкт і викличте для нього метод Print.
Тобто за допомогою зміненого конструктора за замовчуванням, ми можемо констролювати ініціалізацію об“єктів класу і уникати помилок з невизначеними значеннями полів.
Конструктор, який ми створили є «конструктором з параметрами».
І ще один момент, на який хочу звернути вашу увагу: незважаючи на те, що конструктор є паблік-методом, звернення до нього напряму через об“єкт немає, як до методів класу. Він викликається тільки при створенні об“єкта класу, тільки тоді можна з ним якось взаємодіяти. Але якщо ви його помістите в приват-сектор, то… правильно, компілятор вам скаже, що у вас конструктора взагалі немає.
Конструктор в класі може бути не один. Власне кажучи, їх кількість обмежена тільки потребами програми і здоровим глуздом програміста. :)
За допомогою конструкторів можна ініціазізувати клас різними способами, залежно від потреб. Тобто для кожного способу ми пишемо свій конструктор і, власне кажучи, в залежності від опису відповідно змінюється поведінка об“єктів класу. Це і є проявом поліморфізму.
Якщо ми в нашому класі опишемо конструктор такого вигляду:
Cats ()
{
};
То в результаті отримаємо неправильні, тобто, сміттєві значення полів: наш кіт не буде мати імені але матиме фантастичний вік :). В деяких випадках це не має значення, але припустимо, що нам в нашому класі потрібно, щоб об“єкт «за замовчуванням» був з визначеними конкретними значеннями полів. Наприклад, щоб нашого «відправної точки» - кота точно звали Simone, а вік його був 2. Як це зробити?
Cats ()
{name=«Simone»;
age=2;
};
Тепер в нашому коді точно визначені вихідні значення полів об“єкта. Їх ініціалізує наш відкоректований конструктор «по замовчуванню».
class Cats //Об“являємо та описуємо клас котів
{private:
string name;
int age;
public:
string GetName ()
{return name;}
int GetAge ()
{return age;}
Cats ()
{name=«Simone»;
age=2;
};
void SetName (string sName)
{name=sName;}
void SetAge (string sAge)
{name=sAge;}
void Print ()
{ cout<<cat name is - \t“<<name<<“\tcat age is - \t“<<age<<endl;}
};
іnt main ()
{
Cats cat1;
cat1.Print();
return 0;}
Результатом буде рядок в консолі: cat name is — Simone cat age is — 2.
Але в нас клас котів, вони різні і нам треба змінювати їх імена та вік.
Додаємо наш написаний вже конструктор з параметрами:
Cats (string c_name, int c_age)
{name=c_name;
age=c_age;
};
class Cats //Об“являємо та описуємо клас котів
{private:
string name;
int age;
public:
string GetName ()
{return name;}
int GetAge ()
{return age;}
Cats ()
{name=«Simone»;
age=2;
};
Cats (string c_name, int c_age)
{name=c_name;
age=c_age;
};
void SetName (string sName)
{name=sName;}
void SetAge (string sAge)
{name=sAge;}
void Print ()
{ cout<<cat name is - \t“<<name<<“\tcat age is - \t“<<age<<endl;}
};
іnt main ()
{
Cats cat1;
Cats cat2(«Tishka», 1);//Створюємо новий об“єкт на основі конструктора з параметрами і також викликаємо для нього метод Print
cat1.Print();
cat2.Print();
return 0;}
На виході отримуємо інформацію про обидва об“єкти.
Як це працює?
Коли ми створюємо об“єкт cat1, «вмикається» наш конструктор «по замовчуванню», який просто видає ті вихідні значення полів, які в ньому вказані. Таким чином ми контролюємо поведінку нашого об“єкту при його створенні, за допомогою лише конструктору «за замовчуванням».
Коли ми створюємо об“єкт cat2, конструктор з параметрами, який ми створили, обумовлює іншу поведінку об“єкта класу: він приймає значення імені та віку та надає їх своїм полям.
Так у нас працює перевантаження конструкторів, аналогічно з перевантаженням функцій, власне, тому що конструктор по суті і є функцією.
Так проявляється поліморфізм класу: різна поведінка об“єктів в залежності від різних ситуацій.
Звичайно, конструктори — це не всі прояви цього принципу, принцип поліморфізма значно глибший і в подальшому ми розглянемо інші його прояви.
А поки що спробуйте самостійно додати до програми конструктор, який буде ініціалізувати об“єкт нашого класу з параметрами name та color (ім“я та колір).
Завершення і буде вашим домашнім завданням.
Творчої наснаги! :)