Деструктори
Конструктор викликається при створенні об’єкту. Як ми вже знаємо з вивчення об’єктних можливостей TurboPascal, існує також інша підпрограма (у випадку С++ функція), яка автоматично викликається при знищенні об’єкта і називається деструктором. Ім’я деструктора співпадає з іменем конструктора (і класу), тільки починається знаком ~
class foo
{private:
int data;
public:
Foo(): data(0) //конструктор
{ }
~Foo() //деструктор
{ }
};
Як і конструктори, деструктори не повертають значення. Ще вони не мають аргументів. Найпоширеніше використання деструкторів – вивільнення пам’яті, виділеної конструктором при створенні об’єкту, отже, найкраще їх використовувати при роботі з динамічними об’єктами.
Об’єкти в якості аргументів функції
Модернізуємо програму 11.3 так, щоб продемонструвати кілька нових аспектів створення класів: перезавантаження конструкторів, визначення методу класу поза класом і використання об’єктів у якості аргументів функції.
Далі приведений лістінг програми 11.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;
}
Програма 11.5
Перезавантажені конструктори
Було б зручно здійснювати ініціалізацію змінних типу Distance в момент їх створення. Щоб це зробити, викличемо конструктор так:
Distance(int ft,float in):feet(ft),inches(in)
{ }
Ми ініціалізуємо поля feet та inches тими значеннями, які передаються конструктору в якості аргументів.
Разом з тим, ми хочемо зберегти можливість визначати змінні типу Distance без ініціалізації, як ми це робили в програмі 11.3.
Distance dist1,dist2;
В програмі 11.3 не було конструктора, але визначення працювали без помилок. Чому так відбувалося? Це пояснюється тим, що компілятор автоматично вбудовує в програму конструктор без параметрів, який і створює змінні класу, хоча явного визначення конструктора ми не робили. Такий конструктор без параметрів називається конструктором за замовчуванням.
Часто нам хотілося б, щоб початкові значення полів об’єкта присвоювалися також і в конструкторі без параметрів. Якщо покласти цю функцію на конструктор за замовчуванням, то ми не зможемо дізнатися, якими значеннями були ініціалізовані поля. Якщо для нас істотно, якими значеннями будуть ініціалізуватися поля об’єктів класу, то слід явно визначити конструктор. В програмі 11.5 ми вчинили це так:
Distance():feet(0),inches(0.0) //конструктор без аргументів
{ }
Члени класу ініціалізуються константними значеннями, в даному випадку цілим значенням 0 для поля feet і дійсним значенням 0.0 для поля inches. Отже, ми можемо використовувати об’єкти, ініціалізовані за допомогою конструктора без параметрів, будучи певними в тому, що поля об’єктів мають нульове, а не довільне значення.
Тепер у нас є два явно визначені конструктори з тим самим іменем Distance(), і тому кажуть, що конструктор є перезавантаженим. Який з цих двох конструкторів виконається під час створення нового об’єкта, залежить від того, скільки аргументів використовується при виклику:
Distance dist; //конструктор без параметрів
Distance dist2(11,6.25); //конструктор з 2 параметрами
Визначення методів класу поза класом та операція глобального дозволу
Досі ми завжди визначали методи класу всередині класу. Насправді це не обов’язково. В програмі 11.5 функція add_dist() визначена пізніше класу Distance(). Її код
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;
}
Заголовок функції містить новий для синтаксичний елемент:
void Distance::add_dist(Distance d2,Distance d3)
Перед заголовком функції стоїть ім’я класу Distance і символ :: Цей символ є знаком операції глобального дозволу. Така форма запису встановлює взаємозв’язок функції та класу, до якого відноситься ця функція. В даному випадку запис Distance::add_dist( ) означає, що функція add_dist() є методом класу Distance. Синтаксис подібних виразів проілюстрований на рисунку:
Операція дозволу
Об’єкти в якості аргументів
Розглянемо, яким чином виконується програма 11.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.
Підсумовуючи раніше сказане, ми можемо твердити, що кожен виклик методу класу обов’язково зв’язаний з конкретним об’єктом цього класу (за винятком виклику статичних функцій, які ми розглянемо пізніше). Метод може прямо звертатися за іменем до будь-яких закритих і відкритих членів цього об’єкту. Крім того, метод має непрямий (через операцію крапки) доступ до членів інших об’єктів свого класу: ці останні виступають в якості аргументів методу.
Конструктор копіювання за замовчуванням
Ми розглянули два способи ініціалізації об’єктів. Конструктор без аргументів може ініціалізувати поля об’єкту константними значеннями, а конструктор, що має хоч один аргумент, може ініціалізувати поля значеннями, переданими йому як аргументи. Розглянемо третій спосіб ініціалізації об’єкту, що використовує значення полів вже існуючого об’єкта. Для цього не потрібно самим створювати спеціальний конструктор, оскільки такий конструктор надається компілятором для кожного існуючого класу і називається копіюючим конструктором за замовчуванням. Копіюючий конструктор має єдиний аргумент, який є об’єктом такого ж класу, що й конструктор. Програма 11.6 демонструє використання копіюючого конструктора за замовчуванням.
#include <iostream.h>
#include <conio.h>
#include <bios.h>
class Distance
{private:
int feet;
float inches;
public:
Distance():feet(0),inches(0.0) //Конструктор без аргументів
{cout<<”c1”<<endl; }
//Конструктор з 2 аргументами
Distance(int ft,float in):feet(ft),inches(in)
{cout<<”c2”<<endl;}
void getdist()
{cout <<”\nВведіть число футів “; cin >>feet;
cout << “Дюймів “; cin>>inches;
}
void showdist()
{cout <<feet << “\’ “<< inches <<”\’’”;}
};
int main()
{
clrscr();
Distance dist1(11,6.25); //конструктор з 2 аргументами
Distance dist2(dist1); //два конструктори з одним аргументом
Distance dist3=dist1;
cout <<”\ndist1=”;dist1.showdist();
cout << “\ndist2=”;dist2.showdist();
cout << “\ndist3=”;dist3.showdist();
cout <<endl;
bioskey(0);
return 0;
}
Програма 11.6
Ми ініціалізували об’єкт dist1 за допомогою конструктора з двома аргументами. Потім ми визначаємо ще два об’єкти класу Distance з іменами dist2 і dist3, вони обидва ініціалізуються значенням об’єкту dist1. В обох випадках був викликаний копіюючий конструктор за замовчуванням. Об’єкт dist2 ініціалізований за допомогою оператора:
Дата добавления: 2015-08-26; просмотров: 742;