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

