- •2.1 Елементи концепції ооп .. 20
- •1.1 Коментарі.
- •1.2 Прототипи функцій.
- •1.3 Операція розширення області видимості.
- •1.4 Оголошення в операторах.
- •1.5 Перегрузка функцій.
- •1.6 Значення формальних параметрів по замовчуванню.
- •1.7 Посилання та вказівники.
- •1.8 Специфікатор inline
- •1.9 Операції new та delete .
- •1.10 Вказівник на void.
- •1.11 Зв’язування із збереженням типів
- •1.12 Про структури та об’єднання.
- •2.1 Елементи концепції ооп.
- •2.3 Опис протоколу класу.
- •2.4 Передача повідомлень об’єктам.
- •3 Функції-члени.
- •3.1 Функції-члени в межах та за межами формального опису класу.
- •3.2 Про вказівник this.
- •3.3 Перевантаження функцій-членів. Параметри по замовчуванню.
- •4. Конструктори та деструктори.
- •4.1 Поняття про конструктори.
- •4.2 Деструктори.
- •4.3 Досягнення високої ефективності. Конструктор копіювання.
- •5 Глобальні та локальні об’єкти.
- •6 Статична пам’ять та класи.
- •7. Наслідування
- •7.1 Синтаксична реалізація наслідування
- •7.2 Правила доступу до полів даних
- •7.3 Конструктори та деструктори в похідних класах
- •7.4 Використання заміщуючих функцій-членів.
- •7.5 Похідні класи та вказівники.
- •7.6 Ієрархія типів
- •7.7 Множинне наслідування
- •8 Вiртуальнi функцiї та класи
- •8.1 Віртуальні функції.
- •8.2 Чисті віртуальні функції. Абстрактні класи.
- •8.3 Віртуальні деструктори.
- •8.4 Посилання як засіб для реалізації поліморфізму
- •8.5 Технічна реалізація механізму віртуальних функцій.
- •8.6 Віртуальні базові класи
- •8.6.1 Ієрархії класів та наслідування
- •8.6.2 Віртуальні базові класи
- •8.6.3 Виклик конструкторів та віртуальні базові класи.
- •9 Друзі
- •9.1 Дружні класи.
- •9.2 Дружні функції.
- •10 Перевантаження операторiв.
- •10.1 Перевантаження операторів. Загальний підхід.
- •10.2 Перетворення типів.
- •10.3 Перевантаження деяких операторів.
- •10.3.1 Оператор індексування масиву.
- •10.3.2 Перевантаження оператора виклику функції.
- •10.3.3 Оператор доступу до члена класу.
- •10.3.4 Перевантаження операторів інкремента та декремента.
- •10.3.5 Перевантаження операторів управління пам’яттю (new,delete).
- •10.3.6 Перевантаження оператора присвоювання.
- •11.1 Функціональні шаблони
- •11.1.1 Визначення та використання шаблонів функцiй.
- •11.1.2 Перевантаження шаблонiв функцiї.
- •11.1.3 Cпецiалiзованi функцiї шаблона.
- •11.2 Шаблони класів.
- •11.2.1 Визначення шаблонів класу
- •11.2.2 Константи та типи як параметри шаблону
- •11.2.3 Використання шаблонних класів
- •11.2.4 Спецiалiзацiя шаблонiв класу.
- •11.3 Шаблони та конфiгурацiя компiлятора.
- •11.3.1 Шаблони Smart.
- •11.3.2 Шаблони Global I External.
- •12.2 Переадресація вводу-виводу
- •12.3 Розширення потоків для типів кориcтувача
- •12.4 Операції роботи з потоком як дружні
- •12.5 Форматований ввід-вивід
- •12.5.1 Ширина поля
- •12.5.2 Заповнюючий символ
- •12.5.3 Число цифр дійсних чисел
- •12.5.4 Прапорці форматування
- •12.5.5 Маніпулятори
- •12.6 Стан потоку
- •12.7 Файловий ввід-вивід
- •12.7.1 Конструктори файлових потокiв
- •12.7.2 Вiдкриття файлу
- •12.8 Неформатований ввід-вивід
- •12.9 Деякі функції вводу-виводу
- •12.10 Форматування в пам’яті
- •13 Управління виключеннями
- •13.1 Виключення та стек
- •13.2.1 Синтаксис основних конструкцій
- •13.2.1.1 Використання try та сatch
- •13.2.1.2 Використання throw
- •13.2.2 Тип виключення та конструктор копії
- •13.2.3 Пошук відповідного типу виключення
- •13.2.4 Використання terminate() та некеровані виключення
- •13.2.5 Робота з специфікаціями виключень
- •13.2.6 Робота з непередбаченими виключеннями
- •13.2.7 Робота з конструкторами та виключеннями
- •13.2.8 Динамічні об’єкти
- •13.2.9 Передача значень з конструктора та деструктора
- •13.2.10 Робота з ієрархіями виключень
- •13.2.11 Робота з специфічними класами виключень
- •13.3 Структурне управління виключеннями
- •13.3.1 Використання кадрованого управління виключеннями
- •13.3.1.1 Синтаксис
- •13.3.1.2 Про функцію RaiseException()
- •13.3.1.3 Фільтруючий вираз
- •13.3.1.4 Перехоплення виключення процесора
- •13.3.2 Використання завершуючих обробників виключень
4. Конструктори та деструктори.
4.1 Поняття про конструктори.
Розглянемо описаний вище клас Ttime. Оголосимо екземпляр класу v:
TTime v;
При такому оголошеннi відбувається лише виділення пам’яті для відповідного екземпляра класу. Програміст повинен сам подбати про ініціалізацію полів. Якщо уявити собі наявність кількох класів, що мають складну структуру, велику кількість полів даних, то задача акуратної та правильної ініціалізації полів є досить громіздкою та помилконебезпечною. Адже в такому випадку необхідно тримати в пам’яті всю структуру цих класів. В деяких випадках при створенні об’єкту необхідним є і виділення пам’яті. У зв’язку з цим в С++ введені функції-члени, що мають особливий синтаксис , завдання та назву. Це конструктори. Вони служать для ініціалізації полів даних, виділення пам’яті, якщо це необхідно, та виконання інших дій, які можуть бути потрібними при створенні екземпляра об’єкту. Саме слово конструктор - від англійського слова constuct -будувати, конструювати.
Специфіка синтаксису задання конструктора полягає в тому, що його ім’я обов’язково повинне співпадати з іменем класу, в якому він оголошується. При оголошенні конструктора не вказується тип значення, яке він вертає (ключове слово типу результату відсутнє). Конструктор може мати довільну кількість параметрів ( навіть не мати їх взагалі). Його тіло може бути описаним як в межах формального опису класу, так і за його межами (як і для звичайної функції-члена). Конструктор викликається автоматично системою С++ у випадку, коли, виконуються умови його виклику. Для того, щоб викликався конструктор при оголошенні екземпляра об’єкту, необхідно після ідентифікатора змінної-екземпляра в круглих дужках вказати список фактичних параметрів конструктора (так, ніби викликається функція з іменем змінної-екземпляра та параметрами, які повинен мати конструктор). Якщо параметри відсутні, після ідентифікатора нічого не ставиться.
Приклад:
clas TT {
private:
long dt;
char*dts;
publiс:
TT(int, int);
};
TT ::TT(int a, int n)
{dt=a;
dts=(char*)malloc(n*sizeof(char));
}
main()
{
TT t2(1,15);
// . . .
}
При оголошенні TT t2(1,15); зразу буде викликаний конструктор класу, який ініціалізує поле dt та виділяє пам’ять. Умовою його виклику є відповідність параметрів, вказаних в дужках, сигнатурі конструктора. Якщо в нашому прикладі оголосити змінну так: TT t2(1,1); , то буде видане повідомлення про помилку. Адже конструктора з відповідною сигнатурою не існує.
В класах може бути оголошено кiлька конструкторiв в межах правил перевантаження функцій, допускається використання параметрів по замовчуванню. Cпецифiчну роль вiдiграють конструктори, якi мають порожнiй список параметрiв. Такi конструктори називаються конструкторами по замовчуванню. Якщо такий конструктор в класi є, то вiн завжди автоматично викликається при входi екземплера класу в область видимостi.
Приклад:
class TT {
private:
long dt;
char*dts;
publiс:
TT();
};
TT ::TT(void)
{
dt=0;
dts=(char*)malloc(10*sizeof(char));
}
main()
{
TT v;
// . . . .
}
В момент оголошення змінної-екземпляра класу v відбувається виклик конструктора по замовчуванню та відбуваються всі дії, які він виконує.
Відмітимо, що відповідні конструктори класів викликаються і в ситуаціях, коли необхідним є неявне перетворення типів (див. п.12.2).
Якщо конструктор вiдсутнiй, то такий об'єкт можна iнiцiалiзувати шляхом присвоєння йому об'єкта цього ж класу.
TTime today;
.
:
TTime d=today;
При цьому вiдбувається побiтове (до версії 2.0) чи почленне рекурсивне копiювання. Слід обережно працювати з класами, які мають конструктори, оскільки при присвою-ванні екземплярів таких класів можуть виникати помилкові ситуації. Розглянемо наступний приклад:
class IntArray
{
private:
int *data;
unsigned size;
public:
IntArray(void);
IntArray(unsigned mySize);
void put(unsigned index, int value);
int get(unsigned index);
};
int IntArray::get(unsigned index)
{
if (index<size) return data[index];
else return 0;
}
void IntArray:: put(unsigned index, int value)
{ if (index<size) data[index]=value; }
IntArray:: IntArray(void)
{
data=new int[100];
size=100;
for(int i=0;i<size;i++) data[i]=0;
}
IntArray IntArray(unsigned mySize)
{
data=new int[mySize];
size=mySize;
for(int i=0;i<size;i++) data[i]=0;
}
main()
{
IntArray myArray(200);
IntArray second(200);
second=myArray;
myArray.put(10,-50);
cout<<second.get(10);
}
В результаті буде надруковане значення 10-того елемента масиву -50. Тобто для даних об’єктів виділяється одна і та ж область пам’яті. Цю проблему можна легко вирішити, якщо перевантажити оператор присвоювання (див. розділ “Перевантаження операторів” ).
Розглянемо наступний фрагмент програми:
Ttime t1;
Ttime t2(1,2,1998);
t1=t2;
В останньому рядку не викликається жоден конструктор класу. Якщо в класі оголошуються чи наслідуються члени-вказівники на дані, то скопійовані вказівники будуть вказувати на одну і ту ж область пам’яті, що приведе до серйозних помилок, коли ця пам’ять звільняється за допомогою деструктора.
Відмітимо, що якщо в класі немає жодного конструктора, компілятор сам створює функцію-конструктор класу. Цей конструктор викликається в момент створення екземпляра класу (виділення для нього пам’яті) і виконує ініціалізацію вказівника this.