Скачать презентацию Переопределение операций 1 Зачем нужна перегрузка операций Скачать презентацию Переопределение операций 1 Зачем нужна перегрузка операций

Лекция4. Перегрузка_операций_в_С++.ppt

  • Количество слайдов: 24

Переопределение операций 1 Переопределение операций 1

Зачем нужна перегрузка операций? class Complex { double re; double im; public: Complex(double r=0, Зачем нужна перегрузка операций? class Complex { double re; double im; public: Complex(double r=0, double i=0): re(r), im(i){} double get. Re(){return re; } double get. Im(){return im; } }; void main() { Complex c 1(5, 6); Complex c 2(8, 99); Complex c 3; // c 3 = c 1 + c 2; } 2

Что нельзя перегружать . – селектор члена структуры * - оператор доступа к члену Что нельзя перегружать . – селектор члена структуры * - оператор доступа к члену по указателю : : - оператор разрешения видимости ? : - условный тернарный оператор sizeof – определение размера # - препроцессорные операции 3

Различают: • Бинарные • Унарные Переопределяют: • Функции-члены класса • Внешние функции Пусть @ Различают: • Бинарные • Унарные Переопределяют: • Функции-члены класса • Внешние функции Пусть @ - операция, а w, x, y – объекты. Тогда @W эквивалентно w. operator @() operator @(w) x @ y эквивалентно x operator @ (y) operator @ (x, y) 4

Перегруженные операции как функции-члены class Complex { double re; double im; public: Complex(double r=0, Перегруженные операции как функции-члены class Complex { double re; double im; public: Complex(double r=0, double i=0): re(r), im(i){} double get. Re(){return re; } double get. Im(){return im; } Complex operator + (const Complex & c) { Complex rez; rez. re = re + c. re; rez. im = im + c. im; return rez; } Complex operator – () const { Complex rez = *this; rez. re = -rez. re; return rez; } }; void main() { Complex c 1(5, 6); Complex c 2(8, 99); Complex c 3; c 3 = c 1 + c 2; // c 3 = c 1. operator + ( c 2); c 3 = -c 2; // c 3 = c 2. operator – (); } 5

Перегруженные операции как внешние функции Complex operator – (const Complex & r 1, const Перегруженные операции как внешние функции Complex operator – (const Complex & r 1, const Complex & r 2) class Complex { Complex rez; { rez. re = r 1. re - r 2. re; double re; rez. im = r 1. im - r 2. im; double im; return rez; public: } Complex(double r=0, double i=0): re(r), im(i){} Complex operator ! (const Complex & r 1) double get. Re(){return re; } { Complex rez; double get. Im(){return im; } rez. re = -r 1. re; Complex operator + (const Complex & c) rez. im = r 1. im; { void main() return rez; Complex rez; { } rez. re = re + c. re; Complex c 1(5, 6); rez. im = im + c. im; Complex c 2(8, 99); return rez; Complex c 3; } c 3 = c 1 + c 2; Complex operator – () const // c 3 = c 1. operator + ( c 2); { c 3 = -c 2; Complex rez = *this; // c 3 = c 2. operator – (); rez. re = -rez. re; c 3 = c 1 - c 2; return rez; // c 3 = operator - (c 1, c 2); } c 3 = !c 2; friend Complex operator – (const Complex & r 1, const // c 3 = operator ! (c 2); Complex & r 2); } friend Complex operator ! (const Complex & r 1); 6 };

Вопрос «на засыпку» class Complex { double re; double im; public: Complex(double r=0, double Вопрос «на засыпку» class Complex { double re; double im; public: Complex(double r=0, double i=0): re(r), im(i){} double get. Re(){return re; } double get. Im(){return im; } Complex operator + (const Complex & c) { Complex rez; rez. re = re + c. re; rez. im = im + c. im; return rez; } Complex operator – () const { Complex rez = *this; rez. re = -rez. re; return rez; } friend Complex operator – (const Complex & r 1, const Complex & r 2); friend Complex operator ! (const Complex & r 1); Complex operator + ( double d) { Complex rez = *this; rez. re = -rez. re + d; return rez; } }; void main() { Complex c 1(5, 6); Complex c 2(8, 99); Complex c 3; c 3 = c 1 + c 2; // c 3 = c 1. operator + ( c 2); c 3 = -c 2; // c 3 = c 2. operator – (); c 3 = c 1 - c 2; // c 3 = operator - (c 1, c 2); c 3 = !c 2; // c 3 = operator ! (c 2); с3 = с2 + 10; с3 = 10 + c 2; } friend Complex operator – (const double r 1, const Complex & r 2); Complex operator – (const double r 1, const Complex { Complex rez = r 2; rez. re = rez. re + d; return rez; } 7

Оператор присваивания Ø Присваивание по умолчанию Ø Не наследуется Ø Определяется как функция-член класса Оператор присваивания Ø Присваивание по умолчанию Ø Не наследуется Ø Определяется как функция-член класса void main() { String s 1("abcdef"); String s 2("1234"); String s 3; s 3 = s 1; s 2 = s 2; s 3 = s 1 = s 2; } class String { char * str; int len; public: String() {str = NULL; len = 0; } String(char * s){len = strlen(s); str = new char[len + 1]; strcpy(str, s); } String(const String &s){len = s. len; str = new char[len + 1]; strcpy(str, s. str); } String & operator = (const String & s) { if ( this == &s) return *this; delete str; len = s. len; str = new char[len + 1]; strcpy(str, s. str); return *this; } }; 8

Вопрос «на засыпку» void main() { String s 1( Вопрос «на засыпку» void main() { String s 1("abcdef"); String s 2("1234"); String s 3; s 3 = s 1; s 2 = s 2; s 3 = s 1 = s 2; String s 4 = s 1; } В чем разница? 9

Префиксные и постфиксные операторы class X { int value; public: X& operator ++() //префиксный Префиксные и постфиксные операторы class X { int value; public: X& operator ++() //префиксный { value++; return *this; } X operator ++(int) //постфиксный { X t = *this; value ++; return t; } }; void main() { X x; X y = x++; // y. value = ? X z = ++x; // z. value = ? } 10

Оператор индексирования Оператор бинарный a = b[10]; // a = b. operator[](10); //a = Оператор индексирования Оператор бинарный a = b[10]; // a = b. operator[](10); //a = b[5][6]; - так нельзя Ø Оператор – функция-член класса Ø Оператор индексирования ассоциируется с массивами char & operator[] (int i) Ø { class String if (( i >= 0 ) && ( i < (int)len )) return str[i]; { char * str; } int len; }; void main() public: { String() String s 1("abcdef"); {str = NULL; len = 0; String s 2("1234"); } String s 3; String(char * s){len = strlen(s); str = new char[len + 1]; strcpy(str, s); } s 3 = s 1; String(const String &s){len = s. len; str = new char[len + 1]; s 2 = s 2; strcpy(str, s. str); } String & operator = (const String & s) s 3 = s 1 = s 2; { char a = s 3[2]; if ( this == &s) return *this; s 3[2] = 's'; delete str; len = s. len; str = new char[len + 1]; strcpy(str, s. str); return *this; } 11 }

class Arr. String { const int max; char ** pstr; int cur; public: Arr. class Arr. String { const int max; char ** pstr; int cur; public: Arr. String(int j): max(j), cur(0){pstr = new char *[max]; } void add(char * to. Add) void main() { if ( cur < max) pstr[cur++] = to. Add; { } String s 1("abcdef"); char * operator[](int i) String s 2("1234"); { if ((i >= 0)&&(i < cur)) return pstr[i]; return NULL; String s 3; } s 3 = s 1; int operator[](char * to. Find) s 2 = s 2; { s 3 = s 1 = s 2; for ( int i = 0; i < cur; i++) if ( strcmp(pstr[i], to. Find)) char a = s 3[2]; return i; s 3[2] = 's'; return -1; Arr. String arr(5); } }; arr. add("123"); arr. add("qqqqq"); arr. add("aaaaaa"); int i = arr["123"]; char* p = arr[2]; } 12

Оператор вызова функций Ø Оператор – функция-член класса Ø Функция может перегружаться, различие – Оператор вызова функций Ø Оператор – функция-член класса Ø Функция может перегружаться, различие – в сигнатуре функций class A { int i, j; public: тип возврата operator()(список параметров); }; A a 1; a 1(список фактических параметров); // a 1. operator()(список фактических параметров); 13

class String { char * str; size_t len; public: String() {str = NULL; len class String { char * str; size_t len; public: String() {str = NULL; len = 0; } String(char * s){len = strlen(s); str = new char[len + 1]; strcpy(str, s); } String(const String &s){len = s. len; str = new char[len + 1]; strcpy(str, s. str); } String & operator = (const String & s) { if ( this == &s) return *this; delete str; void main() len = s. len; str = new char[len + 1]; strcpy(str, s. str); { return *this; String s 1("abcdef"); } String s 2("1234"); char& operator[] (int i) { String s 3; if (( i >= 0 ) && ( i < (int)len )) return str[i]; s 3 = s 1; } s 2 = s 2; char & operator()(int i) { return str[i]; } s 3 = s 1 = s 2; char * operator()(int i, int j) char a = s 3[2]; {return &str[i+j]; } s 3[2] = 's'; }; s 3(2) = 'b'; char *ch = s 3(1, 1); } 14

Переопределение операции -> Ø Оператор – функция-член класса Ø Возвращает указатель на структуру или Переопределение операции -> Ø Оператор – функция-член класса Ø Возвращает указатель на структуру или объект класса int main() { YPtr y(66); y->a = 99; // (y. operator ->())->a y->b = 77; return 0; } class point{ public: int a, b; }; class YPtr { point *py; public: YPtr(const int a) { py = new point(); py->a = py->b = a; } point * operator ->() { return py; } ~YPtr(){delete py; } }; 15

Перегрузка new и delete Оператор new должен иметь следующий прототип: void * new(size_t size, Перегрузка new и delete Оператор new должен иметь следующий прототип: void * new(size_t size, …); #include Ø Оператор new может перегружаться Ø Операция new всегда статическая функция Ø Оператор delete должен иметь прототип: void delete(void * ) Ø Оператор delete не может перегружаться Ø Оператор delete – статическая функция Ø 16

class Blanks { public: Blanks(){} void *operator new( size_t st, char ch. Init ); class Blanks { public: Blanks(){} void *operator new( size_t st, char ch. Init ); }; void *Blanks: : operator new( size_t st, char ch. Init ) { void *pv. Temp = malloc( st ); if( pv. Temp != 0 ) memset( pv. Temp, ch. Init, st ); return pv. Temp; } int main() { Blanks *a 5 = new( 0 xa 5 ) Blanks; return a 5 != 0; } 17

#include <iostream> void main() using namespace std; { class Stack 2 Stack 1 st #include void main() using namespace std; { class Stack 2 Stack 1 st 1; { //Stack 2 st 2; - нельзя, почему? int top_num; Stack 2 * st 2 = new ( 20) Stack 2; public: st 1. push(6); st 1. push(7); void * operator new(size_t s, int sz = 100) st 1. push(2); { st 1. push(66); return new char[s + sz* sizeof (int)]; } st 2 ->push(6); Stack 2(){top_num = 1; } st 2 ->push(7); void push(int i){*((int *)this + top_num++) = i; } st 2 ->push(2); st 2 ->push(66); int pop(){return *((int *)this + --top_num); } cout << st 1. pop() << " "<< }; st 1. pop() << " "<< st 1. pop()<< 'n'; class Stack 1 cout << st 2 ->pop() << " "<< { st 2 ->pop() << " "<< st 2 ->pop()<< 'n'; int * pst; } int size; int count; public: Stack 1(int s=100): size(s){pst = new int [size]; count = 0; } ~Stack 1(){ delete pst; } void push( int value) { if ( count < size ) pst[count++] = value; return; } int pop() { return pst[--count]; } }; 18

class A { int* s 1, * s 2, * s 3; public: void class A { int* s 1, * s 2, * s 3; public: void * operator new(size_t s, size_t n) { return : : new int[ s+3*n ]; } void operator delete(void * p) { : : delete p; } }; void main() { A * a 1 = new(55) A; //… delete a 1; } 19

class B { int i; public: int operator , (int k) { return i class B { int i; public: int operator , (int k) { return i +k; } B(int _i){i = _i; } }; void main() { B b 1(3); int k = (b 1, 8); } 20

Приведение типов Ø С помощью конструктора Ø С помощью оператора приведения типов 21 Приведение типов Ø С помощью конструктора Ø С помощью оператора приведения типов 21

Приведение типов с помощью конструктора class A; class B { int b; public: B(int Приведение типов с помощью конструктора class A; class B { int b; public: B(int i){b = i; } friend class A; }; class A { int a; public: A(int i){a = i; } A(const B& b 1){a = b 1. b; } }; void main() { B bb(99); A aa = bb; } 22

Приведение типов с помощью операций приведения operator тип (); Ø Функция – член класса Приведение типов с помощью операций приведения operator тип (); Ø Функция – член класса Ø Возвращает объект типа тип Ø class A; class B { int b; public: B(int i){b = i; } friend class A; }; class A { int a; public: A(int i){a = i; } A(const B& b 1){a = b 1. b; } operator B(){B b 1(a+99); return b 1; } operator int(){ return 55*a; } }; void main() { B bb(99); A aa = bb; B x = aa; int k = aa; } 23

Правила перегрузки типов Ø Ø Ø Ø Вводить собственные обозначения для операций, не совпадающие Правила перегрузки типов Ø Ø Ø Ø Вводить собственные обозначения для операций, не совпадающие со стандартными операциями языка С++, нельзя. Каждая операция, заданная в языке, имеет определенное число операндов, свой приоритет и ассоциативность. Все эти правила, установленные для операций в языке, сохраняются и для ее перегрузки, т. е. изменить их нельзя. Любая унарная операция определяется двумя способами: либо как компонентная функция без параметров, либо как глобальная (возможно дружественная) функция с одним параметром. Выражение z означает в первом случае вызов z. operator (), во втором - вызов operator (z). Любая бинарная операция определяется также двумя способами: либо как компонентная функция с одним параметром, либо как глобальная (возможно дружественная) функция с двумя параметрами. В первом случае x y означает вызов x. operator (y), во втором – вызов operator (x, y). Перегруженная операция не может иметь аргументы (операнды), заданные по умолчанию. В языке С++ установлена идентичность некоторых операций, например, ++z – это тоже, что и z+=1. Эта идентичность теряется для перегруженных операций. Функцию operator можно вызвать по ее имени, например, z=operator*(x, y) или z=x. operator*(y). В первом случае вызывается глобальная функция, во втором – компонентная функция класса Х, и z – это объект класса Х. Однако, чаще всего функция operator вызывается косвенно, например, z=x*y За исключением перегрузки операций new и delete функция operator должна быть либо нестатической компонентной функцией, либо иметь как минимум один аргумент (операнд) типа “класс” или “ссылка на класс” (если это глобальная функция). Операции ‘=’, ‘[]’, ‘–>’, () можно перегружать только с помощью нестатической компонентной функции operator. Это гарантирует, что первыми операндами будут леводопустимые выражения. Перегрузка операций ‘++’ и ‘--‘, записываемых после операнда (z++, z--), отличается добавлением в функцию operator фиктивного параметра int, который используется только как признак отличия операций z++ и z-- от операций ++z и --z. Глобальные операции new можно перегрузить и в общем случае они могут не иметь аргументов (операндов) типа “класс”. В результате разрешается иметь несколько глобальных операций new, которые различаются путем изменения числа и (или) типов аргументов. Глобальные операции delete не могут быть перегружены. Их можно перегрузить только по отношению к классу. Для правильного освобождения динамической памяти под базовый и производный объекты следует использовать виртуальный деструктор. Если для класса Х операция “=” не была перегружена явно и x и y - это объекты класса Х, то выражение x=y задает по умолчанию побайтовое копирование данных объекта y в данные объекта x. 24