Похожие презентации:
Л3.Ч1. Перегрузки (1)
1. 6. Перегрузка операций
Перегрузка функцийПерегрузка функций ‒ одно из проявлений полиморфизма в С++.
Перегрузка функций - это использование одинаковых
имен для однотипных функций.
ПРИМЕР. Пусть требуется написать 3 функции вывода:
• массива a из m целых чисел;
• длинного целого числа;
• строки символов.
2.
Начинаем работу с «придумывания имен», например: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)
Поскольку список формальных аргументов у каждой
функции разный, т.е. отличается количеством и/или
типом (говорят - сигнатурой), то при вызове функций
компилятор по списку фактических аргументов
разберется, какой экземпляр функции требуется
вызвать:
3.
Print(“Hello!”);Print(a, 20);
Print( 50000l );
// функция Print(char *)
// функция Print(int *, int)
// функция Print(long)
/*суффикс I подчеркивает, что число следует
рассматривать как long*/
Конструкторы – полиморфизм
Определение нескольких конструкторов в классе - это
перегрузка функций, т.е. проявление полиморфизма
для член-функций класса.
4. Возможна двусмысленность!
int sum2(int a, int b)Причина двусмысленности:
{ return a+b; }
в стандарте языка имеется
float sum2(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 = sum2(a, b); cout << “c = ” << c;
двусмысленность
z = sum2(3.5, -1.2);
// Error: Ambiguity between 'sum2(int,int)’ and
// 'sum2(float,float)‘
Компилятор сомневается,
z = sum2(x, y); // true
не применить ли его здесь?!
cout << “z = ” << z;
Решает, что это не в его
компетенции и формирует
}
ошибку!
5. В таких ситуациях достаточно одной функции
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
cout << “c = ” << c;
z = sum2(3.5, -1.2);
cout << “z = ” << z;
z = sum2(x, y);
cout << “z = ” << z;
}
6. Перегрузка операций
В С++ можно выполнить перегрузку операций дляобъектов класса.
То есть с помощью знаков операций
+, -, *, и т.д.
можно определить похожие действия для абстрактных
типов данных.
7. Двуместная операция
Формат перегрузки двуместной операции:Тип operator @ (операнд_2) {тело_операции},
где Тип ‒ тип возвращаемого значения,
@ - знак двуместной операции.
Первым операндом является объект, в классе которого
эта операция определяется, т.е. * this.
Второй операнд – произвольный (операнд_2).
Используется перегруженный знак так же, как для
стандартных типов данных
операнд1 @ операнд2
8. Пример 1
В классе String вместо функции Plus можноопределить операцию ‘+=’ :
String& String :: operator +=( String &s2 )
{ char *t = new char[ len+1 ];
ctrcpy_s(t, len+1, line);
// сохраним временно 1-й операнд!
delete [] line; len = len + s2.len;// новая длина
line = new char[ len+1 ];
strcpy_s(line, len+1, t);
strcat_s(line, len+1, s2.line);
delete [] t;
return *this;
}
operator +=
вместо имени ч/функции Plus
9. Пример использования
В примере из п.5 вместо оператораString *s3 = new String(s1.Plus(s2));
можно записать
String *s3= new String(s1+=s2);
Еще пример использования:
String s(“Студент “), r(“Петров “);
s += r; // s = “Студент Петров”
10. Пример 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(”Строки не равны”);
Ответ?
11. Перегрузка сравнения ==
Но было бы нагляднее дляиспользовать операцию = =.
Перегрузим ее для класса String:
сравнения
строк
int String :: operator ==(String &s)
{if(strcmp(line, s.line)) return 0;
return 1; }
Cравнение теперь выглядит привычнее:
if (s1 == s2) puts(”\n Строки равны”);
else {s1. Print(); printf(” - это не “); s2.Print();}
Ответ?
String s1(“Иванов”), s2(“Петров”);
operator ==
Иванов – это не Петров
вместо имени ч/функции EqStr
12. Не забыть:
При описании класса эти перегруженныеоперации надо объявить
class String { ...
public:
...
String& operator +=( String &);
int operator ==(String &);
...
};
13. Одноместная операция
Формат перегрузки одноместной операции:где
пусто
Тип operator @() {тело_операции}
Тип ‒ тип возвращаемого значения
@ ‒ знак одноместной операции.
Обращение:
@ операнд
У одноместной операции единственный операнд и это
*this!
14. Пример 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();
// вывод «нофелет»
15. Задача
Задано слово. Является ли слово «перевертышем»?Напомню:
void main()
приоритет одноместных операций
выше!
{ String s1(“казак”);
String s2 = s1; // Работает конструктор копирования,
// чтобы не изменить s1 операцией ~
s1.Print();
if(s1 == ~s2) puts(” – перевертыш”);
else puts(” - не перевертыш”);
}
Ответ?
казак - перевертыш
16. Правила перегрузки:
1. При перегрузке операции, как член-функции класса,двуместная операция имеет один аргумент,
одноместная – ни одного;
2. Знак одноместной операции может быть перегружен только как одноместный, а двуместной –
только как двуместный;
3. Наряду с обычным использованием перегруженного знака obj1 @ obj2 для двуместной операции
и @ obj
для одноместной он может использоваться как член-функция класса
obj1.operator @(obj2) и obj.operator @()
17.
4. Нельзя перегружать операции для стандартныхтипов данных
Например, + для массивов, определенных, как
int * a или int a[20].
5. Нельзя перегружать операции
::
.
?:
sizeof
18. 7. Примеры перегрузки некоторых операций
1. Перегрузка [ ]Пусть определен объект
String s(«Еденица»);
Заметив ошибку, попытаемся её исправить:
s[2] = ’и’;
// ошибка: операция [ ] в классе
// String не определена
19. Следовательно ее надо определить
Для этого вместо функции Index (см п. 1), определимоперацию [ ]:
Трактуется компилятором
как двуместная операция
char & String :: operator [ ] ( int i)
{ if (i<0 || i>=len) {puts(“Индекс за пределами строки»);
exit(0);}
return line[i];
}
В этом случае можно записать оператор
s[2] = ’и’; //это возможно благодаря char &, ‘Единица’
или вывести символ
cout << s[0]; // вывод буквы ‘Е’
Не забудем объявить её в классе
char & operator [ ] (int );
20. 2. Перегрузка операции =
Если объект использует динамическую область, то длянего надо перегрузить операцию ‘= ‘ - присвоение.
Рассмотрим почему.
Пусть заданы 2 объекта:
String s1, s2(“ФПМК”);
...
s1 = s2;
21.
Картинаприсвоения
напоминает
инициализацией (даже хуже):
ситуацию
с
До присвоения:
line
s1:
line
s2:
80 байт
len
0
\0
len
4
s1 = s2;
После присвоения:
line
s1:
line
s2:
len
4
len
4
...
Так как компилятор выполняет
5 байт
простое копирование
Ф П М К \0
s1.line = s2.line;
s1.len = s2.len
и память брошена
80 байт
\0
...
и оба объекта используют
одну и ту же память
5 байт
Ох, как жаль
Ф П М К \0
эти 80 байтов!
22. Это недопустимо по следующим причинам:
1. Память в 80 байтов у объекта s1 будет «брошена»(считаться занятой);
2. Объекты s1 и s2 будут использовать одну и ту же
динамическую память по указателю line, что
приведет к тому, что любое изменение в поле line
объекта s1 приведет к изменению поля line объекта
s2 и наоборот;
3. При выходе из функции деструктор будет пытаться
дважды освободить одну и ту же динамическую
память: это фатальная ошибка!
23.
В классах, где используется динамическая память,операция ‘=’ обязательно перегружается.
24. Пример перегрузки операции = для класса String
String & String :: operator =(const String &s){ if( this != &s)
// на случай присвоения s = s
{ delete [ ] line; // Важно освободить
// динамическую память у *this
line = new char [(len = s.len)+1];
// сразу определим поле len
strcpy_s(line, len+1, s.line);
}
return *this;
}
25. Теперь присвоение s1 = s2 будет выполняться грамотно.
Основные действияоперации =
line
delete [ ] line;
// 1)
line = new char [(len = s.len)+1]; // 2)
strcpy( line, s.line);
// 3)
len
Пустая строка из 80 байтов
4 0
\0
s1:
Ф
line
s2:
...
П
М
К
2)
\0
3)
len
4
1)
Ф
П
М
К
\0
куча
Все неприятности исчезнут!
26. Конструктор копирования и операция присвоения =
String s(“Лето”), r(“Солнышко”);String p = s;
// работает конструктор копирования
…
r = s;
// работает перегруженная операция =
27. Отличие операции = и конструктора копирования
Оператор = выполняет 3 действия :1. Освобождает динамическую память у левого объекта
(её могло быть меньше или больше, чем у правого);
2. берёт новую динамическую память такого же размера,
как у правого объекта;
3. копирует поля правого объекта в поля левого.
Конструктор копирования выполняет 2 действия:
1. Берет динамическую память для левого объекта такого
же размера, как у правого;
2. копирует поля правого объекта в поля левого.
Конструктор копирования не может выполнить освобождение памяти у левого объекта, так как у него её ещё и
НЕ БЫЛО!
28. 3. Перегрузка операции ()
Если объект - матрица, то для обращения к ееэлементам нельзя перегрузить [][].
В этом случае можно использовать перегрузку
операции ().
class Matrix
{ int **a, m, n;
public:
Matrix( int m1 = 1, int n1 = 1, int t = 0, int d = 10);
// конструктор с аргументами по умолчанию
~Matrix();
void Show(); // вывод матрицы
int & operator()(int, int);
...
};
29. Конструктор
Matrix::Matrix(int mm, int nn, int t, int d)// mm – строк, nn – столбцов, d - диапозон
// t != 0 – генерировать случайные числа
{m = mm; n = nn; int i, j;
a = new int * [m];
for( i = 0; i<m; i++)
a[i] = new int [n];
if(t) for(i = 0; i<m; i++)
for(j = 0; j<n; j++)
a[i][j] = rand()%d;
}
30. Перегрузка ()
int & Matrix :: operator()(int i, int j){ if(i<0 || i>=m || j<0 || j>=n)
{puts("\n Значения индексов недопустимы. Выход.”);
exit(1);
}
return a[ i ][ j ];
}
Возвращаемое значение - ссылка int & - для того,
чтобы иметь возможность
менять значения элементов матрицы.
31. Пример использования
void main(){ srand(time(0));
Matrix A(3,4), B(3,3,1);
// A не инициализируется
// случайными числами,
// B – инициализируется
for ( int i = 0; i<3; i++)
B(i, i) = 1;
// занесение 1 на главную диагональ
puts("\nB:“);
B.Show();
...
}
32. Замечание
Операция () - единственная, которая может иметьпроизвольное количество аргументов (в частности 0).
ПРИМЕР
void Matrix:: operator()()
// обнулить матрицу
{ int i, j;
for ( i = 0; i<m; i++)
for ( j = 0; j<n; j++)
a[i][j] = 0;
}
ИСПОЛЬЗОВАНИЕ
Matrix B(5, 5);
B();
Программирование