Л3.Ч1.This.Перегрузка операций.ppt
- Количество слайдов: 29
п 4. Неявный указатель this Каждый объект класса имеет свою копию членданных, но один экземпляр каждой членфункции для всех объектов. Как член-функция “понимает”, с член-данными какого объекта она работает? С теми, которые принадлежат объекту, вызвавшему эту функцию. Например, s 2. Print(); // с член-данными объекта s 2
this Говорят, что в функцию в этом случае передается неявный указатель на этот объект. Его можно задать и явно с помощью ключевого слова this. Например, void Print(){ printf(“%s”, this->line); }
Но бывают ситуации (кстати, довольно частые при использовании ООП), когда приходится задавать этот указатель явно.
Пример Определим функцию, которая будет к первой строке приписывать вторую и результатом возвращать первую (конкатенация строк), объявив ее в классе String Plus(String &);
и определив ее за классом: String : : Plus(String &s 2) {char *t = new char[ len+1]; strcpy(t, line); delete []line; len = len+s 2. len; // новая длина line = new char[ len+1]; strcpy(line, t); strcat(line, s 2. line); delete [] t; return *this; //возвращаем «этот» объект Вернуть надо} 2 поля line и len. Но так return line, len; нельзя!
Пример использования функции Plus: String s 1(“Объект “), s 2(“класса String. ”); String *s 3 = new String(s 1. Plus(s 2)); // работает функция Plus, // а затем конструктор копирования // при создании динамического объекта s 3 ->Print(); // вывод *s 3 = ”Объект класса String. ”;
п 5. Перегрузка операций New. Перегрузка функций. Это одно из проявлений полиморфизма в С++. Перегрузка функций - это использование одинаковых имен для однотипных функций. Рассмотрим на примере. Пусть требуется написать 3 функции вывода: • массива a из m целых чисел; • длинного целого числа; • строки символов.
void Printm (int *a, int m) для массива, Printl (long n) для длинного целого, Prints (char *s) для строки. В С++ все эти 3 функции могут быть заданы одним именем void Print (int *a, int m) Print (long n) Print (char *s)
Список формальных аргументов у каждой функции разный, т. е. отличается количеством и/или типом (говорят - сигнатурой). При вызове функций компилятор по списку фактических аргументов разберется, какой экземпляр функции требуется вызвать. Print(“Hello!”); // функция Print(char *) Print(a, 20); // функция Print(int *, int) Print( 50000 l ); // функция Print(long)
Конструкторы - полиморфизм Заметим, что определение нескольких конструкторов в классе - это перегрузка функций, т. е. проявление полиморфизма для член-функций класса.
Возможна двусмысленность! int sum 2(int a, int b) Причина двусмысленности: { return a+b; } в стандарте языка имеется float sum 2(float a, float b) преобразование по умолчанию float -> int { return a+b; } void main() { int a = 4, b = 5, c; float x = 3. 5, y = 2, z; c = sum 2(a, b); printf(“nc = %d”, c); // z = sum 2(3. 5, -1. 2); двусмысленность // Error: Ambiguity between 'sum 2(int, int)’ and // 'sum 2(float, float)‘ Компилятор сомневается, z = sum 2(x, y); // true не применить ли его здесь? ! printf(“nz = %f”, z); Решает, что это не в его компетенции и формирует } ошибку!
В таких ситуациях достаточно одной функции float sum 2(float a, float b) { return a + b; } void main() { int a = 4, b = 5, c; float x = 3. 5, y = 2, z; c = sum 2(a, b); //действует преобразование int ->float printf(“nc = %d”, c); z = sum 2(3. 5, -1. 2); printf(“nz = %f”, z); z = sum 2(x, y); printf(“nz = %f”, z); } Перегрузка функций закончилась
Перегрузка операций В С++ можно выполнить перегрузку операций для объектов класса, то есть с помощью знаков операций +, -, *, и т. д. можно определить похожие действия для абстрактных типов данных
Двуместная операция Формат перегрузки двуместной операции тип_возвр_знач. operator @ (тип_операнд_2) {тело_операции}, где @ - знак двуместной операции. Первым операндом является объект, в классе которого эта операция определяется, т. е. * this, второй операнд – произвольный (операнд_2). операнд 1 @ операнд 2
Пример 1 В классе String вместо функции Plus можно определить операцию ‘+=’ String& String : : operator +=( String &s 2 ) { char *t = new char[ len + 1 ]; strcpy(t, line); // сохраним временно первый операнд! delete [] line; len = len + s 2. len; // новая длина line = new char[ len + 1 ]; strcpy( line, t); strcat( line, s 2. line); delete [] t; operator += return *this; } вместо имени ч/функции Plus
Пример использования В примере из п. 4 вместо оператора String *s 3 = new String(s 1. Plus(s 2)); можно записать String *s 3= new String(s 1+=s 2); String s(“Студент “), r(“Петров “); s += r; // s = “Студент Петров”
Пример 2 В классе String определим функцию сравнения двух строк int String : : Eq. Str(String &s) {if(strcmp(line, s. line)) return 0; //строки не равны return 1; // строки равны } Использовать ее можно таким образом String s 1(“Иванов”), s 2(“Петров”); if ( s 1. Eq. Str(s 2)) puts(”Строки равны”); else puts(”Строки не равны”); Ответ?
Перегрузка сравнения == Но было бы нагляднее для сравнения строк использовать операцию = =. int String : : operator ==(String &s) {if (strcmp(line, s. line)) return 0; return 1; } if(s 1 == s 2) puts(”n Строки равны”); else {s 1. Print(); printf(” - это не “); s 2. Print(); } Ответ? String s 1(“Иванов”), s 2(“Петров”); == operator Иванов – это не Петров вместо имени ч/функции Eq. Str
Не забыть При описании класса эти перегруженные операции надо объявить class String {. . . public: . . . String& operator +=( String &); int operator ==(String &); . . . };
Одноместная операция Формат перегрузки одноместной операции пусто тип_возвращаемого_знач. operator @() {тело_операции} где @ - знак одноместной операции. Обращение @ операнд У одноместной операции единственный операнд и это *this!
Пример 3 Определим операцию реверса строки, т. е. перестановки символов в обратном порядке. String : : operator ~() {int i; char t; for(i = 0; i < len / 2; i++) {t = line[i], line[i] = line[ len – i – 1]; line[len – i – 1] = t; } return *this; }
Пример использования String r( «телефон» ); ~r; r. Print(); // вывод «нофелет»
Задача Задано слово. Является ли слово «перевертышем» ? Напомню: приоритет одноместных операций void main() выше! { String s 1(“казак”); String s 2 = s 1; // Работает конструктор // копирования, чтобы не // изменить s 1 операцией ~ s 1. Print(); if(s 1 == ~s 2) puts(” – перевертыш”); else puts(” - не перевертыш”); Ответ? } казак - перевертыш
Правила перегрузки: 1. При перегрузке операции, как членфункции класса, двуместная операция имеет один аргумент, одноместная – ни одного; 2. Знак одноместной операции может быть перегружен только как одноместный, а двуместной - только как двуместный;
Правила перегрузки (продолжение): 3. Наряду с обычным использованием перегруженного знака obj 1 @ obj 2 для двуместной операции и @ obj для одноместной он может использоваться как член-функция класса obj 1. operator @(obj 2) и obj. operator @()
Правила перегрузки (окончание): 4. Нельзя перегружать операции для стандартных типов данных Например, + для массивов, определенных, как int * a или int a[20]. 5. Нельзя перегружать операции : : . ? : sizeof
п 6. Примеры перегрузки некоторых операций 1. Перегрузка [ ] Пусть определен объект String s( «Еденица» ); s[2] = ’и’; // ошибка: операция [ ] в классе // String не определена
Объяснимся Действительно, объект может иметь несколько полей данных типа «массив» и компилятору неизвестно, к какому из массивов мы хотели бы применить операцию [ ].
Следовательно ее надо определить Для этого вместо функции Index (см п. 2), Трактуется компилятором определим операцию [ ] как двуместная операция char & String : : operator [ ] ( int i) { if (I < 0 || I >= len) {puts(“Индекс за пределами строки» ); exit(0); } return line[i]; } s[2] = ’и’; // и это возможно благодаря char &, ‘Единица’ putchar(s[0]); // вывод буквы ‘Е’ Не забудем объявить её в классе char & operator [ ] (int );
Л3.Ч1.This.Перегрузка операций.ppt