Лабораторна робота № 5 Перевантаження операторів Мета роботи: познайомитися із перевантаженням операторів. Короткі теоретичні відомості Перевантаження операторів Кожному оператору мова С++ ставить у відповідність ім'я функції, що складається з ключового слова operator, власне оператору та аргументів відповідних типів: тип operator [символ оператору] (списокПараметрів) { //тіло метода }
Щоб використовувати операцію над об'єктами класів, ця операція повинна бути перевантажена, але є два виключення. Операція присвоювання (=) може бути використана з кожним класом без явного перевантаження. За замовчуванням операція присвоювання зводиться до побітового копіювання даних-елементів класу. Проте таке побітове копіювання небезпечне для класів з елементами, що вказують на динамічно виділені області пам'яті; для таких класів слід явно перевантажувати операцію присвоювання. Операція адресації (&) також може бути використана з об'єктами будь-яких класів без перевантаження; вона просто повертає адресу об'єкта в пам'яті. Але операцію адресації можна також і перевантажувати. Перевантаження операцій підпорядковується наступним правилам: При перевантаженні зберігаються кількість аргументів, пріоритети операцій та правила асоціації, що використовуються у стандартних типах даних; Для стандартних типів даних операції не підлягають перевизначенню; Перевантажена функція-оператор не може мати параметрів по замовчуванню, не успадковується та не може бути визначеною як static; Функція-оператор може бути визначена трьома способами – метод класу, дружня функція або звичайна функція. В останніх двох випадках вона повинна приймати хоча б один аргумент, що має тип класу, покажчика або посилання на клас. При перевантаженні операцій ( ), [], -> або = функція перевантаження операції повинна бути оголошена як елемент класу. Для інших операцій функції перевантаження операцій можуть не бути функціями-елементами. Коли функція-операція реалізована як функція-елемент, крайній лівий (або єдиний) операнд повинен бути об'єктом того класу (або посиланням на об'єкт того класу), елементом якого є функція. Якщо лівий операнд повинен бути об'єктом іншого класу або убудованого типу, така функція-операція не може бути реалізована як функція-елемент. Функція-операція, реалізована не як функція-елемент, повинна бути другом, якщо ця функція повинна мати прямий доступ до закритих або захищених елементів цього класу. Щоб оголосити функцію як друга (frіend) класу, перед її прототипом в описі класу ставиться ключове слово frіend. Дружні функції класу визначаються поза областю дії класу, але мають право доступу до закритих елементів класу. Перевантажена операція << повинна мати лівий операнд типу ostream & (такий, як cout), так що вона не може бути функцією-елементом. Аналогічно, перевантажена операція >> повинна мати лівий операнд типу іstream & (такий, як cіn), так що вона теж не може бути функцією-елементом. До того ж кожна з цих перевантажених функцій-операцій може забажати доступу до закритих елементів-даних об'єкта класу, так що ці перевантажені функції-операції роблять функціями-друзями класу. Будь-яку бінарну операцію можна перевантажувати як нестатичну функцію-елемент з одним аргументом, або як функцію, що не є елементом, із двома аргументами (один з цих аргументів повинен бути або об'єктом класу, або посиланням на об'єкт класу). Унарну операцію класу можна перевантажувати як функцію-елемент без аргументів, або як функцію, що не є елементом, з одним аргументом; цей аргумент повинен бути або об'єктом класу, або посиланням на об'єкт класу. Функції-елементи, що реалізують перевантажені операції, повинні бути нестатичними, щоб вони могли мати доступ до даних класу. Нагадаємо, що статичні функції-елементи можуть мати доступ тільки до статичних даних-елементів класу. При перевантаженні унарних операцій переважно створюють функції-операції, що є елементами класу, замість дружніх функцій, що не є елементами. Дружніх функцій краще уникати доти, поки вони не стануть абсолютно необхідними. Використання друзів порушує інкапсуляцію класу. Щоб перевантажити операцію інкремента та декремента для одержання можливості використання і префіксної, і постфіксної форм, кожна з цих двох перевантажених функцій-операцій повинна мати різну сигнатуру, щоб компілятор мав можливість визначити, яка версія мається на увазі в кожному конкретному випадку. Префіксний варіант перевантажується як будь-яка інша префіксна унарна операція. Для постфіксної форми вводиться додатковий параметр цілого типу у список аргументів, щоб зробити функцію для постфіксного варіанту відмінною від функції для префіксної форми. Зауваження щодо перевантаження операцій: Існують обмеження на перевантаження: не підлягають цій процедурі селектор елемента структури (.), оператор доступу до елементу за покажчиком (*), операція дозволу видимості (::), символи препроцесору (#, ##) та sizeof(). Неможливим є введення власних операторів. Компілятор С++ не розуміє семантики перевантаженого оператору, а отже, не нав'язує жодних математичних концепцій. Можна перевантажити, скажімо, оператор інкременту в якості зменшення аргументу, проте навряд чи в цьому є сенс. Не існує виведення складних операторів з простих: якщо ви перевантажили оператори operator+ та operator=, це зовсім не означає, що С++ обчислить вираз a += b, оскільки ви не перевантажили operator +=. Перевантаження бінарних операторів не тотожньо відносно перестановки аргументів місцями, тим більше, якщо вони різного типу. // Array.h: interface for the Array class. #include<iostream.h> #include<stdlib.h> #include<time.h> class Array { friend ostream& operator<< (ostream& output, Array& arr); int* ptr; int size; public: Array(int s = 10); Array(Array& arr); virtual ~Array(); void Rindomize(int num = 10); Array& operator= (Array& arr); Array& operator+= (Array& arr); Array operator+ (Array& arr) ; int operator!= (Array& arr); Array operator++ (); Array operator++ (int); }; // Array.cpp: implementation of the Array class. #include "Array.h" Array::Array(int s) { size = s; ptr = new int[size]; for(int i = 0; i < size; i++) { ptr[i] = 0; } cout << "Constructor" << endl; } //-------------------------------------------------------------------------------- Array::Array(Array& arr) { size = arr.size; ptr = new int[size]; for(int i = 0; i < size; i++) { ptr[i] = arr.ptr[i]; } cout << "Copy Constructor" << endl; } //-------------------------------------------------------------------------------- Array::~Array() { delete[] ptr; cout << "Destructor" << endl; } //-------------------------------------------------------------------------------- void Array::Rindomize(int num) { for(int i = 0; i < size; i++) { ptr[i] = rand() % num; } } //-------------------------------------------------------------------------------- ostream& operator<< (ostream& output, Array& arr) { for(int i = 0; i < arr.size; i++) { output << arr.ptr[i] << " "; } output << endl; return output; } //-------------------------------------------------------------------------------- Array& Array::operator= (Array& arr) { if(this != &arr) { delete[] ptr; size = arr.size; ptr = new int[size]; for(int i = 0; i < size; i++) { ptr[i] = arr.ptr[i]; } } cout << "Operator =" << endl; return *this; } //-------------------------------------------------------------------------------- int Array::operator!= (Array& arr) { if(size != arr.size) return 1; for(int i = 0; i < size; i++) { if(ptr[i] != arr.ptr[i]) return 1; } return 0; } //-------------------------------------------------------------------------------- Array Array::operator+ (Array& arr) { int mins = (size < arr.size) ? size : arr.size; Array temp; if(mins == arr.size) { temp = *this; for(int i = 0; i < mins; i++) { temp.ptr[i] += arr.ptr[i]; } } else { temp = arr; for(int i = 0; i < mins; i++) { temp.ptr[i] += ptr[i]; } } cout << "Operator +" << endl; return temp; } //-------------------------------------------------------------------------------- Array& Array::operator+= (Array& arr) { Array temp; temp = *this + arr; *this = temp; return *this; } //-------------------------------------------------------------------------------- Array Array::operator++ () { for(int i = 0; i < size; i++) ptr[i] += 1; return *this; } //-------------------------------------------------------------------------------- Array Array::operator++ (int) { Array temp = *this; for(int i = 0; i < size; i++) ptr[i] += 1; return temp; } // Main.cpp : main program #include"Array.h" int main() { srand(time(NULL)); Array a(5); a.Rindomize(5); Array b(7); b.Rindomize(5); Array c; cout << "Array a:" << endl << a; cout << a++; cout << a;
Завдання Описати клас, що реалізовує вказаний нижче тип даних. Клас повинен містити множину конструкторів для створення об'єктів певного типу (конструктор по замочуванню та з параметрами, конструктор копії) та подані у таблиці операції над об'єктами класу (плюс обов'язково операцію присвоювання) з використанням механізму перевантаження операцій. Написати програму, яка демонструє роботу з об'єктами цього класу. Організувати виведення та введення даних за допомогою класів-потоків сin та cout. Варіант Завдання
1 Клас Matrix (матриця квадратна) Операції: - =, * =.
2 Клас Complex (комплексні числа) Операції: - =, * =.
3 Клас Fraction (дроби) Операції: - =, * =.
4 Клас HugeInt (цілі числа) Операції: - =, порівняння(<, >).
5 Клас Fraction (дроби) Операції: + =, / =.
6 Клас Set (множини) Операції: об’єднання множин(+ =), перетин множин (- =).