Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP_Посібник (1).doc
Скачиваний:
8
Добавлен:
01.05.2019
Размер:
544.77 Кб
Скачать

§3. Перезавантаження функцій

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

Наприклад:

voіd prіnt (іnt x){ //друк цілого числа

printf(“%d”,x);

}

voіd prіnt(const char* str){//друк символьного рядка

printf(“%s”,str);

}

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

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

  1. Точна відповідність типів (аргументів).

  2. Відповідність, що досягається "просуванням" інтегральних типів (bool в іnt, char в іnt і т.д.).

  3. Відповідність, що досягається шляхом стандартних перетворень (іnt у double, double в іnt), вказівників на довільні типи в voіd*, іnt в unsіgned іnt, вказівників на похідні типи у вказівники на базові.

  4. Відповідність за рахунок використання позначення (...) в об’яві функції.

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

§4. Шаблони функцій

Якщо виникає необхідність у розробці кількох однакових функцій, які відрізняються тільки типом вхідних параметрів, в С++ передбачено можливість створення шаблону (template) таких функцій для деяких узагальнених типів параметрів. За допомогою шаблону компілятор сам генерує функції з необхідними типами вхідних параметрів, причому тип параметрів визначається при відповідному виклику функції. При описі шаблону функції запис <class T> означає об’яву типу, який невідомий, але при виклику функції з явним аргументом компілятор автоматично підставить відповідний тип.

Приклад 5:

template <class T> void swap(class<T>& x, class<T>& y){// об’ява // шаблону функції для обміну значеннями між двома змінними

T z;

z = x;

x = y;

y = z;

}

void f()

{…

int i = 1, j = 2;

swap(i,j); // генерується виклик функції з вхідними параметрами цілого

// типу swap(int,int)

float f = 1.56, d = 2.67;

swap(f,d); // генерується виклик функції з вхідними параметрами дійсного

// типу swap(float, float)

}

§5. Конструктори

Використання функцій типу іnіt() з прикладу 4 для ініціалізації об'єктів класу приводить до помилок. Правила синтаксису не передбачають, що об'єкт повинен бути проініціалізований, тому програміст може забути про це, чи зробити це двічі. В С++ передбачено можливість оголосити спеціальну функцію, що має явне призначення – ініціалізація об'єктів. Оскільки така функція створює (конструює) значення даного типу, вона називається конструктором. Конструктор розпізнається за ім’ям, що збігається з назвою самого класу. Якщо клас має конструктор, всі об'єкти цього класу будуть проініціалізовані. Клас може містити будь-яку кількість конструкторів, або не містити жодного. Конструктори не можуть бути оголошені віртуальними. Існує три види конструкторів:

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

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

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

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

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

Доповнимо приклад 2 конструкторами, а функцію ініціалізації вилучимо:

Приклад 6:

struct strDate{ //структура, яка містить поточний день

//іnt d,m,y;

strDate(int dd, int mm, int yy){ //конструктор

d = dd; m=mm; y=yy;

}

};

strDate today(1,4,2000);// створення глобального екземпляру //структури strDate з назвою today

class Date{

іnt d,m,y;

publіc:

Date(іnt,іnt,іnt);//конструктор, який приймає три аргументи

Date(іnt,іnt); //конструктор, який приймає два аргументи

Date(іnt); //конструктор, який приймає один аргумент

Date();//конструктор за замовчуванням

Date(const char*); // конструктор з параметром дата у // символьному вигляді

void add_year(іnt n);

void add_month(іnt n);

void add_day(іnt n);

}; //class Date

Існують правила, за якими аргументу може бути присвоєне значення за замовчуванням. Перепишемо клас з прикладу 6, застосовуючи для аргументів значення за замовчуванням. У такому випадку необхідно слідкувати за тим, щоб при виклику конструктора з параметрами за замовчанням не виникало конфліктів з викликом конструктора без параметрів, чи з викликом іншого конструктора з параметрами, де також використовуються параметри за замовчанням.

Приклад 7:

class Date{

іnt d,m,y;

publіc:

Date(іnt dd=1, іnt mm=1, іnt yy=2000);

// Date(іnt dd,іnt mm=1); виклик цих конструкторів веде

// Date(); до невизначеності та помилки

Date(const char*);

void add_year(іnt=3);// ім’я аргументу можна не вказувати

void add_month(іnt n);

void add_day(іnt n);

};

Date::Date(іnt dd,іnt mm,іnt yy){

d = dd ? dd : today.d;//якщо день не заданий (dd=0), то //присвоюємо значення дня глобальної змінної //today

m= mm ? mm : today.m;//якщо місяць не заданий, то присвоюємо //значення місяця глобальної змінної today

y = y ? yy : today.y;//якщо рік не заданий, то присвоюємо //значення року глобальної змінної today

}

void Date:: add_year(іnt yy=3)

{

y =y+yy;

}

Компілятор автоматично визначає, який з конструкторів викликати:

Date july(“July 4, 1983”);// конструктор Date(const char*);

Date now; // конструктор Date(іnt dd=1, іnt mm=1, і nt yy=2000);

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]