З предмету «Об’єктно орієнтоване програмування» 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

З предмету «Об’єктно орієнтоване програмування»



Курс лекцій

З предмету «Об’єктно орієнтоване програмування»

 

Частина 2 – «Об’єктно орієнтоване програмування в мові C ++»


 

 

Зміст

Тема 9 Об’єктно-орієнтоване програмування в C++. Об’єкти і класи в С++

Тема 10. Перезавантаження операцій в С++


Тема 9 Об’єктно-орієнтоване програмування в C++. Об’єкти і класи в С++

Створення простого класу

Визначення класу

Приховування даних

Дані класу

Методи класу

Приховування даних і доступність функцій

Методи класу всередині визначення класу

Використання класу

Повідомлення

Об’єкти програми і об’єкти реального світу

Клас як тип даних

Конструктори

Деструктори

Визначення методів класу поза класом та операція глобального дозволу

Об’єкти в якості аргументів

Конструктор копіювання за замовчуванням

Об’єкти, що повертаються функцією

Класи, об’єкти та пам’ять

Статичні дані класу

Роздільне оголошення і визначення полів класу

Константні методи

Константні об’єкти

Підсумок

Питання по темі

 

 

Мова С++ є класикою об’єктного програмування: вона створювалася саме з метою якомога повніше втілити ООП-підхід. Але, оскільки ми вже ознайомлені з принципами ООП і деякими способами його втілення, то відразу ж розпочнемо зі створення простого об’єктного класу, а потім будемо робити необхідні пояснення. Зауважимо тільки, що об’єктний тип в С++, як і в Delphi, називається класом, конкретні представники цього класу - об’єктами.

 

Створення простого класу

Наш перший приклад містить клас і два об’єкти цього класу. Попри свою простоту, він демонструє синтаксис і основні риси класів. Лістінг програми приведений нижче (програма 9.1)

 #include <iostream.h>

 #include <conio.h>

 #include <bios.h>

 class smallobj

 {private:

 int somedata;

 public:

 void setdata(int d)

 {somedata=d;}

 void showdata()

 {cout <<"Значення поля="<<somedata<< endl;};

 };

 

 int main()

 { clrscr();

 smallobj s1,s2;

 s1.setdata(3066);

 s2.setdata(3776);

 s1.showdata();

 s2.showdata();

 bioskey(0);

 return 0;

 }

 

Програма 9.1

Клас smallobj, визначений у цій програмі, містить одне поле даних і два методи. Методи забезпечують доступ до поля даних класу. Перший з методів присвоює полю значення, а другий метод виводить це значення на екран. Поле оголошене як захищене (private: зверніть увагу на вертикальну двокрапку після ключового слова, вона обов’язкова), метод як відкритий (public:)

В програмі описані дві змінні об’єктного типу, вони є екземплярами класу smallobj.

 

Визначення класу

Визначення класу smallobj в нашій програмі виглядає так:

 

Class smallobj

 {private:

 int somedata; //поле класу

 public:

 void setdata(int d) // метод класу, що змінює значення поля

 {somedata=d;}

 void showdata() //метод класу, що відображає значення поля

 {cout <<"Значення поля ="<<somedata<< endl;};

 };

Визначення починається ключовим словом class, після якого пишеться ім’я класу – в даному випадку smallobject. Тіло класу включене у фігурні дужки, після них стоїть крапка з комою (;). Цей знак є обов’язковим, на відміну від конструкцій, зв’язаних з передачею управління (функцій і циклів), де без нього можна обійтися.

 

Приховування даних

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

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

 

Дані класу

Клас smallobj містить лише одне поле somedata типу int. Дані, що містяться всередині класу, називаються даними-членами або полями класу. Кількість полів класу може бути довільною. Оскільки перед описом поля проставлене слово private, це поле доступне тільки всередині класу.

 

Методи класу

Методи класу – це функції, що входять до складу класу. Клас smallobj містить два методи – somedata і setdata. Оскільки обидва ці методи описані після ключового слова public, вони доступні за межами класу.. Далі приведений синтаксиси визначення класу.

 

Використання класу

Подивимося, як можна використати вже визначений клас в функції main(). Після оператора, який очищає вікно користувача (clrscr()) наступний оператор

smallobj s 1, s 2;

Цей оператор визначає два об’єкти s1 і s2 класу smallobj. При визначенні класу не створюються ніякі його об’єкти, тільки задається вигляд майбутнього об’єкта. Всі операції програма здійснює з об’єктами. Визначення об’єкта схоже на визначення змінної: воно означає виділення пам’яті, необхідної для збереження об’єкта.

Виклик методів відбувається в такій самій формі, як у Паскалі

s1.setdata(3066);

 s2.setdata(3776);

 s1.showdata();

 s 2. showdata ();

Для того, щоб дістати доступ до методу класу, необхідно використовувати операцію крапки (.), що зв’язує метод з іменем об’єкта. Ще цю операцію називають операцією доступу до методу класу.

Оператор

s 1. setdata (3066);

викликає метод setdata об’єкта s1. Метод присвоює полю somedata значення 3066. Подібним способом оператор

s2. setdata(3776);

присвоює полю somedata об’єкта s2 значення 3776. Тепер у нас є два об’єкти з різними значеннями поля somedata.

Аналогічно, два виклики функції showdata() відобразять на екрані значення полів відповідних об’єктів.

s1. showdata();

s2. showdata();

 

Повідомлення

В деяких об’єктно-орієнтованих мовах програмування виклики методів об’єктів називають повідомленнями. Так, наприклад, виклик

s1. showdata();

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

 

Int main()

{

clrscr();

Клас як тип даних

Розглянемо приклад, що демонструє використання об’єктів С++ в якості змінних типу, визначеного користувачем. Об’єкти будуть представляти відстані, виражені в англійській системі мір. Далі приведений лістінг програми (програма 9.3)

# include < iostream. h>

# include < conio. h>

#include <bios.h>

Class Distance

{private:

int feet;

float inches;

public:

Void getdist()

{cout <<”\nВведіть число футів “; cin >>feet;

cout << “Дюймів “; cin>>inches;

}

Void showdist()

{cout <<feet << “\’ “<< inches <<”\’’”;}

};

Int main()

{Distance dist1,dist2; //дві довжини

clrscr ();

Конструктори

Програма 9.3 демонструє два способи використання методів класу для ініціалізації полів об’єкту класу. Як правило, зручніше ініціалізувати поля об’єкта автоматично в момент його створення, а не явно викликати в програмі відповідний метод. Такий спосіб ініціалізації реалізується за допомогою особливих методів класу – конструкторів. Конструктор – це метод класу, що виконується автоматично в момент створення об’єкта.

В якості прикладу конструктора створимо клас «лічильник». Лічильник – це засіб, призначений для збереження кількісної міри якої-небудь величини, що змінюється. Він може зберігати число звертань до файлу, число разів, коли користувач натиснув клавішу Enter, число клієнтів банку і т.д. Як правило, при настанні відповідної події лічильник збільшується на 1 (інкрементується). Звертання до лічильника відбувається, як правило, для того, щоб дізнатися про поточне значення величини Для вимірювання якої він призначений.

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

 

#include <iostream.h>

#include <conio.h>

#include <bios.h>

Class Counter

{private:

unsigned int count;

public:

Int main()

{

clrscr();

Автоматична ініціалізація

Коли створюється об’єкт типу Counter, хотілося б, щоб його поле count було ініціалізоване нульовим значенням, оскільки більшість лічильників починає свій відлік саме з нуля. Ми могли б провести ініціалізація за допомогою функції set_count() з аргументом, рівним 0, чи створити спеціальний метод zero_count, який обнулює значення функції. Недолік такого підходу полягає в тому, що ці функції необхідно викликати явно кожен раз при створенні об’єктів типу Counter:

Counter c1;

c1.zero_count();

Подібні дії легко можуть спричинити неправильну роботу всієї програми, оскільки програмісту для цього достатньо забути проініціалізувати хоч одну змінну після її створення. Якщо в програмі створюється багато таких змінних, значно простіше і надійніше було б ініціалізувати їх відразу ж після створення. В програмі 9.4 цю дію виконує конструктор Counter(). Конструктор викликається автоматично при створенні кожного з об’єктів. Таким чином, в функції main() оператор

Counter c1,c2;

створює два об’єкти типу Counter. При створенні кожного з них викликається конструктор Counter(), що присвоює полю сount нульове значення. Таким чином, крім створення змінних, даний оператор ще й присвоює їх полям початкове (нульове) значення.

 

Ім’я конструктора

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

 

Список ініціалізації

Найчастіше на конструктор покладається задача ініціалізації полів об’єкту класу. Для кожного об’єкту класу Counter конструктор виконує ініціалізацію поля count нулем. Але це робиться не звичайним способом, наприклад:

Counter()

{count=0; }

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

Деструктори

Конструктор викликається при створенні об’єкту. Як ми вже знаємо з вивчення об’єктних можливостей TurboPascal, існує також інша підпрограма (у випадку С++ функція), яка автоматично викликається при знищенні об’єкта і називається деструктором. Ім’я деструктора співпадає з іменем конструктора (і класу), тільки починається знаком ~

class foo

{private:

int data;

public:

Foo(): data(0) //конструктор

{ }

~Foo()         //деструктор

{ }

};

Як і конструктори, деструктори не повертають значення. Ще вони не мають аргументів. Найпоширеніше використання деструкторів – вивільнення пам’яті, виділеної конструктором при створенні об’єкту, отже, найкраще їх використовувати при роботі з динамічними об’єктами.

 

Об’єкти в якості аргументів функції

Модернізуємо програму 9.3 так, щоб продемонструвати кілька нових аспектів створення класів: перезавантаження конструкторів, визначення методу класу поза класом і використання об’єктів у якості аргументів функції.

Далі приведений лістінг програми 9.5

#include <iostream.h>

#include <conio.h>

#include <bios.h>

class Distance

{private:

int feet;

float inches;

public:

 

Distance():feet(0),inches(0.0) //конструктор без аргументів

{ }

 

//конструктор з 2 аргументами

Distance(int ft,float in):feet(ft),inches(in)

{ }

 

void getdist()

{cout <<”\nВведіть число футів “; cin >>feet;

cout << “Дюймів “; cin>>inches;

}

 

void showdist()

{cout <<feet << “\’ “<< inches <<”\’’”;}

 

void add_dist(Distance,Distance); //прототип

};

//dodavanna d2 i d3

void Distance::add_dist(Distance d2,Distance d3)

{

 inches=d2.inches+d3.inches;

 feet=0;

 if(inches>=12.0)

 {inches-=12.0;

 feet++;}

 feet+=d2.feet+d3.feet;

}

 

int main()

{

clrscr();

Distance dist1,dist3;

Distance dist2(11,6.25); //визначення та ініціалізація

dist1.getdist();

dist3.add_dist(dist1,dist2);

cout <<”\ndist1=”;dist1.showdist();

cout << “\ndist2=”;dist2.showdist();

cout << “\ndist3=”;dist3.showdist();

cout <<endl;

bioskey(0);

return 0;

}

Програма 9.5

Об’єкти в якості аргументів

Розглянемо, яким чином виконується програма 9.5. Об’єкти dist1 та dist3 створюються за допомогою конструктора без аргументів, а об’єкт dist2 – за допомогою конструктора, що приймає 2 аргументи, значення яких ініціалізують поля об’єкту dist2. Значення для ініціалізації об’єкту dist1 вводяться користувачем за допомогою методу getdist().

Величини, які ми хочемо додати, передаються в якості аргументів методу add_dist(). Синтаксис передачі об’єктів у функцію такий сам, як і для змінних стандартних типів: на місці аргумента вказується ім’я об’єкта. Оскільки add_dist() є методом класу Distance, він має доступ до будь-яких полів будь-якого об’єкту класу Distance, використовуючи операцію крапки (.), наприклад, dist1.inches чи dist2.feet.

Якщо уважно вивчити функцію add_dist(), побачимо ще кілька важливих деталей, що стосуються методу класу. Методу класу завжди наданий доступ до полів об’єкта, для якого він викликаний: об’єкт зв’язаний з методом операцією крапки (.). Однак насправді методу класу доступні й інші об’єкти. Якщо задатися питанням, до яких об’єктів має доступ метод add_dist(), то, подивившись на виклик

dist3.add_dist(dist1,dist2);

можна помітити, що, крім об’єкту dist3, з якого він викликаний, метод має доступ також до об’єктів dist1 та dist2, які є його аргументами. Можна розглядати об’єкт dist3 в якості псевдоаргументу функції add_dist: формально він не є аргументом, але функція має доступ до його полів. Сенс приведеного виклику можна сформулювати так: «виконати метод add_dist об’єкту dist3». Коли всередині функції проходить звертання до полів inches та feet, це означає, що насправді звертання проходить до полів dist3.inches та dist3.feet.

Функція не повертає значення: типом повернутого значення для неї є void. Результат автоматично присвоюється об’єкту dist3.

Підсумовуючи раніше сказане, ми можемо твердити, що кожен виклик методу класу обов’язково зв’язаний з конкретним об’єктом цього класу (за винятком виклику статичних функцій, які ми розглянемо пізніше). Метод може прямо звертатися за іменем до будь-яких закритих і відкритих членів цього об’єкту. Крім того, метод має непрямий (через операцію крапки) доступ до членів інших об’єктів свого класу: ці останні виступають в якості аргументів методу.

 

Класи, об’єкти та пам’ять

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

 

Статичні дані класу

Познайомившись з тим, що кожен об’єкт класу містить свої власні дані, поглибимо своє розуміння даної концепції. Якщо поле даних класу описане з ключовим полем static, то значення цього поля буде одинаковим для всіх об’єктів даного класу. Статичні дані класу корисні в тих випадках, коли необхідно, щоб всі об’єкти включали в себе якесь одинакове значення. Статичне поле за своїми характеристиками нагадує статичну змінну: воно видиме тільки всередині класу, але час його життя співпадає з часом життя програми. Таким чином, статичне поле існує навіть тоді, коли не існує жодного об’єкта класу. Тим не менше, на відміну від статичної змінної функції, призначеної для збереження значень між викликами, статична змінна класу використовується для зберігання даних, які спільно використовуються об’єктами класу.

Програма 9.8 ілюструє просте використання статичного поля класу:

#include <iostream.h>

#include <conio.h>

#include <bios.h>

class foo

{private:

static int count; //Спільне поле

public:

foo()

{count++;} //Інкрементування при створенні

int getcount()

{return count;}

};

int foo:: count=0; //Визначення count

 

int main()

{clrscr();

foo f1,f2,f3;//Створення 3 обєктів

cout << "Число обєктів " <<f1.getcount()<<endl;

cout << " Число обєктів " <<f2.getcount()<<endl;

cout << " Число обєктів " <<f3.getcount()<<endl;

bioskey(0);

return 0;

}

Програма 9.8

 В цьому прикладі клас foo містить єдине поле count, що має тип static int. Конструктор класу інкрементує значення поля count. В функції main() ми визначаємо 3 об’єкти класу foo. Оскільки конструктор в цьому випадку викликається тричі, інкрементування поля count теж відбувається тричі. Метод getcount() повертає значення count. Ми викликаємо цей метод для кожного з об’єктів і в кожному випадку одержуємо ту саму величину.

Статичні поля класу використовуються значно рідше, ніж автоматичні, однак існують ситуації, коли їх зручно використовувати.

 

Константні методи

Константні методи відрізняються тим, що не міняють значень полів свого класу. Розглянемо це на прикладі:

class aclass

{private:

int alpha;

public:

void nonfunc() //неконстантний метод

{alpha=99;} //коректно

void confiunc() const //константний метод

{alpha=99;} //некоректно: не можна міняти значення поля

};

Звичайний метод nonfunc() може поміняти значення поля, а константний метод confunc() не може. Якщо константний метод спробує поміняти значення поля, компілятор видасть повідомлення про помилку.

Для того, щоб зробити функцію константною, необхідно вказати ключове слово const після прототипу функції, але до початку тіла функції. Якщо оголошення і визначення функції розділені, то модифікатор const необхідно вказати двічі – як при оголошенні функції, так і при її визначенні. Ті методи, які тільки зчитують дані з поля класу, є сенс робити константними, оскільки у них нема необхідності змінювати значення полів об’єктів класу.

Використання константних функцій допомагає компілятору виявляти помилки. Ще за їх допомогою можна створювати і використовувати константні об’єкти.

Проілюструємо це на прикладі, оголосивши константними деякі методи класу Distance програми 9.7. Одержимо програму 9.9

 

#include <iostream.h>

#include <conio.h>

#include <bios.h>

class Distance

{private:

int feet;

float inches;

public:

 

Distance():feet(0),inches(0.0) //конструктор без аргументів

{ }

 

//конструктор з 2 aргументами

Distance(int ft,float in):feet(ft),inches(in)

{ }

 

void getdist()

{cout <<"\nВведіть число футів "; cin >>feet;

cout << "дюймів "; cin>>inches;

}

 

void showdist() const

{cout <<feet << "\' "<< inches <<"\''";}

 

Distance add_dist(const Distance)const; //Прототип

};

//dodavanna d2 i d3

Distance Distance::add_dist(const Distance d2)const

{

Distance temp;

//feet=0; //помилка не можна міняти поле

//d2.feet=0; //помилка не можна міняти поле

temp.inches=inches+d2.inches;

 if(temp.inches>=12.0)

 {temp.inches-=12.0;

 temp.feet++;}

 temp.feet+=feet+d2.feet;

 return temp;

}

 

int main()

{

clrscr();

Distance dist1,dist3;

Distance dist2(11,6.25);

dist1.getdist();

dist3=dist1.add_dist(dist2);

cout <<"\ndist1=";dist1.showdist();

cout << "\ndist2=";dist2.showdist();

cout << "\ndist3=";dist3.showdist();

cout <<endl;

bioskey(0);

return 0;

}

Програма 9.9

В цьому прикладі обидві функції showdist() та add_dist() є константними. В тілі функції add_dist() перший із закоментованих операторів feet=0; демонструє, що компілятор видасть помилку при спробі зміни константною функцією полів об’єкта, з якого вона викликалася.

Константним можна оголосити і аргумент методу. В нашому прикладі це виглядає так:

Distance Distance::add_dist(const Distance d2)const

Функція не може поміняти значення цього аргумента, що і видно на прикладі другого закоментованого оператора d2.feet=0;

 

Константні об’єкти

В кількох попередніх прикладах ми бачили, що ключове слово const можна використовувати для захисту від зміни значень змінних стандартних типів, наприклад, int. Виявляється, аналогічним способом можна застосовувати модифікатор const і для об’єктів класів. Якщо об’єкт класу оголошений з модифікаторм const, він стає недоступним для зміни. Це означає, що для такого об’єкта можна викликати тільки константні методи, оскільки лише вони гарантують, що об’єкт не буде змінений. В якості прикладу розглянемо програму 9.10.

#include <iostream.h>

#include <conio.h>

#include <bios.h>

class Distance

{private:

int feet;

float inches;

public:

//Конструктор з 2 аргументами

Distance(int ft,float in):feet(ft),inches(in)

{ }

void getdist() //Неконстантний метод

{cout <<"\nВведіть число футів "; cin >>feet;

cout << "дюймів "; cin>>inches;

}

 

void showdist() const //Константний метод

{cout <<feet << "\' "<< inches <<"\''";}

};

 

 

int main()

{

clrscr();

const Distance football(300,0);

//football.getdist();

cout <<"Довжина поля=";

football.showdist();

cout <<endl;

bioskey(0);

return 0;

}

 

Програма 9.10

Об’єкт football оголошений як константний, тому для нього можна викликати тільки константні методи, наприклад, showdist(). Виклики неконстантних методів, таких як getdist(), є некоректними.

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

 

Підсумок

Клас являє собою образ, що визначає структуру своїх об’єктів. Об’єкти включають в себе як дані, так і функції, призначені для їх обробки. І дані, і функції можуть бути визначені як закриті, що означає їх доступність тільки для членів даного класу; та як відкриті, тобто доступні для будь-якої функції програми. Закритість членів класу задається ключовим словом private, а відкритість – ключовим словом public.

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

Конструктор – це метод класу, ім’я якого співпадає з іменем класу і який виконується кожен раз при створенні нового об’єкту. Конструктор не має типу повернутого значення, однак може приймати аргументи. Часто конструктор використовується для ініціалізації створюваних об’єктів класу. Конструктори допускають перезавантаження, тому можлива ініціалізація об’єкту кількома способами.

Деструктор – це метод класу, іменем якого є ім’я класу, перед яким проставлений знак ~. Виклик деструкторів відбувається при знищенні об’єкту. Деструктор не має ні аргументів, ні значення, яке повертається.

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


 

Питання по темі

1. У визначенні класу члени класу з ключовим словом private доступні:

а) для будь-якої функції програми

б) для користувачів, які знають пароль

в) методам класу

г) тільки відкритим членам класу

 

2. Операція крапки (операція доступу до члена класу об’єднує такі два елементи (зліва направо):

а) член класу і об’єкт класу

б) об’єкт класу і клас

в) клас і член цього класу

г) об’єкт класу і член цього класу

 

3. Посилання повідомлення об’єкту еквівалентне

а) виклику методу цього об’єкта

б) створенню об’єкта

в) видаленню об’єкта

 

4. Конструктор викликається автоматично в момент _______ об’єкта

а) звертання до об’єкта

б) створення об’єкта

в) видалення об’єкта

г) копіювання об’єкта

 

5. Ім’я конструктора у С++

а) Init

б) Done

в) Create

г) співпадає з іменем класу

 

6. Чи правильне твердження: один клас може мати кілька конструкторів?

а) так

б) ні

в) тільки клас, що не містить константних методів

 

7. Де, як правило, в конструкторі записується ініціалізація полів об’єкта?

а) між прототипом методу і тілом функції

б) в тілі функції

в) після тіла функції

 

8. Вираз Cl cl2=cl1; де Cl – ідентифікатор об’єктного типу, є:

а) оператором присвоювання

б) викликом конструктора копіювання за замовчуванням

в) викликом одного з методів класу

 

9. Деструктори:

а) не мають аргументів і не повертають жодного значення

б) не мають аргументу, але повертають значення у викликаючу програму

в) можуть мати аргументи і не повертають значення у викликаючу програму

 

10. Методу класу доступні дані:

а) об’єкта, членом якого він є

б) класу, членом якого він є

в) будь-якого об’єкту класу, членом якого він є

д) класу, оголошеного відкритим

 

11. Операція глобального дозволу позначається символом:

а)::

б):

в) _

 

 

12. Нехай оголошені три об’єкти класу. Скільки копій полів класу міститься в пам’яті? Скільки копій методів функції?

а) три копії полів і три копії методів

б) три копії полів і одна копія методів

в) одна копія полів і одна копія методів

 

13. Роздільне оголошення і визначення полів класу здійснюється для:

а) закритих полів

б) відкритих полів

в) статичних полів

 

14. Константний метод, викликаний для об’єкту класу:

а) може змінювати як константні, так і неконстантні поля

б) може змінювати тільки неконстантні поля

в) може змінювати тільки константні поля

г) не може змінювати ні константні, ні некостантні поля

 

15. Чи істинне твердження: об’єкт, оголошений як константний, можна використовувати тільки за допомогою константних методів?

 


Тема 10. Перезавантаження операцій в С++

 

Вступ

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

Ключове слово operator

Аргументи операції

Значення, що повертаються операцією

Тимчасові безіменні об’єкти

Постфіксні операції

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

Арифметичні операції

Об’єднання рядків

Множинне перезавантаження

Операції порівняння

Операції арифметичного присвоювання

Операція індексації масиву

Перетворення типів

Перетворення основних типів у основні типи

Перетворення об’єктів у основні типи і навпаки

Перетворення рядків в об’єкти класу String і навпаки

Перетворення об’єктів класів в об’єктів інших класів

Деякі рекомендації щодо перезавантаження операцій і перетворення типів

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

«Підводні камені» перезавантаження операцій і перетворення типів

Підсумок

Питання по темі

 

Вступ

Перезавантаження операцій в - одна з найпривабливіших можливостей ООП, Воно може перетворити складний і малозрозумілий лістінг в інтуїтивно зрозумілий. Наприклад, рядки:

d3.addobjects(d1,d2);

або

d3=d1.addobjects(d2)

можна замінити на більш читабельний вираз

d3=d1+d2;

Термін «перезавантаження операцій» дається звичайним операціям С++, таким як +, -, *, >= чи += у випадку їх використання з визначеними користувачем типами даних.

Звичайно оператори виду

a=b+c;

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

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

Інший вид дій, перетворення типів, тісно зв’язаний з перезавантаженням операцій. С++ здійснює перетворення простих типів, таких як int чи float, автоматично, але перетворення, що включає в себе створені користувачем типи, вимагає виконання певної роботи програмістом.

 

Ключове слово operator

Як використати звичайні операції з визначеними користувачем типами? В цьому оголошенні використане ключове слово operator для перезавантаження операції ++

void operator++()

Спершу пишуть повернутий тип (в нашому випадку void), потім ключове слово operator, потім саму операцію (++) і нарешті список аргументів, взятий у дужки (в даному випадку порожній). Такий синтаксис повідомляє оператору, що коли аргумент належить класу Counter, то потрібно викликати функцію з таким іменем, зустрівши в тексті програми операцію ++.

Компілятор може розрізняти перезавантажувані функції за типом даним та кількістю аргументів (сигнатурою). Перезавантажувані операції компілятор розрізняє за типом даних їх операндів. Якщо операнд має базисний тип, такий як int в операторі

++intvar;

то компілятор буде використовувати свою вбудовану процедуру для збільшення змінної типу int. Але, якщо операнд є об’єктом класу Counter, то компілятор буде використовувати написану програмістом функцію operator++() (нагадаємо, що це явище називається поліморфізмом).

 

Аргументи операції

В функції main() операція ++ застосована до об’єкту, як у виразі ++с1. Досі функція operator++() не мала аргументів. Що ж вона збільшує? Вона збільшує змінну count об’єкта, до якого застосовується. Оскільки методи класу завжди мають доступ до об’єктів класу, для яких були викликані, то ця операція не потребує аргументів.

 

Тимчасові безіменні об’єкти

В програмі 10.2 ми створили тимчасовий об’єкт temp типу Counter, чиєю єдиною метою було збереження значення, яке повертає операція ++. Реалізація цього об’єкту вимагала трьох рядків програми

Counter temp;

temp.count=count;

return temp;

Існує багато способів повертати тимчасовий об’єкт з функції чи перезавантаженої операції. Розглянемо ще один спосіб в програмі 10.3

#include <iostream.h>

#include <conio.h>

#include <bios.h>

class Counter

{private:

unsigned int count;

public:

Counter():count(0) //constructor

{ };

Counter(int c):count(c)

{}

 

unsigned int get_count()

{return count;}

 

Counter operator++()

{++count;

return Counter(count);

}

};

 

int main()

{

clrscr();

Counter c1,c2;

cout << "\nc1="<<c1.get_count();

cout <<"\nc2="<<c2.get_count();

++c1;

c2=++c1;

cout << "\nc1="<<c1.get_count();

cout <<"\nc2="<<c2.get_count();

cout <<endl;

bioskey(0);

return 0;

}

Програма 10.3

В рядку цієї програми

return Counter(count);

відбуваються ті самі дії, які в програмі 10.2 займали три рядки. Тут створюється об’єкт типу Counter. Він не має імені, бо воно ніде не використовуватиметься. Цей об’єкт ініціалізується значенням, одержаним у вигляді параметра count.

Зверніть увагу, що, порівняно з попередньою програмою, додався конструктор з одним параметром

Counter(int c):count(c)

{}

Об’єкт, ініціалізований значенням count, може бути повернутий функцією. Результат цієї програми такий же, як і попередньої..

 

Постфіксні операції

Досі ми застосовували операцію збільшення, використовуючи тільки префіксний запис

++с1

А як можна використовувати постфіксний запис, де змінна збільшується після того, як її значення було використано у виразі с1++?

Щоб мати змогу працювати з двома версіями операції, ми визначимо два варіанти перезавантаження операції ++. Це показано в програмі 10.4

#include <iostream.h>

#include <conio.h>

#include <bios.h>

class Counter

{private:

unsigned int count;

public:

Counter():count(0) //constructor

{ }

Counter (int c):count(с)

{ }

 

unsigned int get_count()

{return count;}

 

Counter operator++ ()

{return Counter(++count);}

 

Counter operator++ (int)

{return Counter(count++);}

 

};

////////////////////

int main()

{

clrscr();

Counter c1,c2;

cout << “\nc1=”<<c1.get_count();

cout <<”\nc2=”<<c2.get_count()<<endl;

++c1;

c2=++c1;

cout <<”\nPrefix”<<endl;

cout << “c1=”<<c1.get_count();

cout <<”\nc2=”<<c2.get_count();

cout <<endl;

c2=c1++;

cout <<”\nPostfix”<<endl;

cout << “c1=”<<c1.get_count();

cout <<”\nc2=”<<c2.get_count();

bioskey(0);

return 0;

}

Програма 10.4

Тепер ми маємо два типи оголошення функції operator++. З однією з них, для префіксної операції, ми вже познайомилися.

Counter operator++ ()

Для реалізації постфіксної операції ++ використаємо нову функцію:

Counter operator++ (int)

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

 

Арифметичні операції

В програмі 11.5 ми розглядали два об’єкти класу Distance, які додавалися за допомогою методу add_dist()

dist3.add_dist(dist1,dist2);

Використовуючи перезавантаження операції +, ми можемо записати подібний вираз зрозуміліше:

dist3=dist1+dist2

Ця можливість реалізована в лістінгу програми 10.5

#include <iostream.h>

#include <conio.h>

#include <bios.h>

class Distance

{private:

int feet;

float inches;

public:

 

Distance():feet(0),inches(0.0) //конструктор без параметрів

{ }

 

//конструктор з 2 параметрами

Distance(int ft,float in):feet(ft),inches(in)

{ }

 

void getdist()

{cout <<”\nВведіть число футів “; cin >>feet;

cout << “дюймів “; cin>>inches;

}

 

void showdist()

{cout <<feet << “\’ “<< inches <<”\’’”;}

 

Distance operator+(Distance) const;

};

 

//Додавання d2 I d3

Distance Distance::operator+(Distance d2) const

{

 int f=feet+d2.feet;

 float i=inches+d2.inches;

 if(i>=12.0)

 {i-=12.0;

 f++;}

 return Distance(f,i);

}

 

int main()

{

clrscr();

Distance dist1,dist3,dist4;

dist1.getdist();

 

Distance dist2(11,6.25);

 

dist3=dist1+dist2;

dist4=dist1+dist2+dist3;

cout <<”\ndist1=”;dist1.showdist();

cout << “\ndist2=”;dist2.showdist();

cout << “\ndist3=”;dist3.showdist();

cout << “\ndist4=”;dist4.showdist();

cout <<endl;

bioskey(0);

return 0;

}

Програма 10.5



Поделиться:


Последнее изменение этой страницы: 2021-12-15; просмотров: 64; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.141.27.244 (0.567 с.)