Май 2007 1 Перегрузка операторов и функций. Май 20072 Перегрузка функций Бывает удобно, чтобы функции, реализующие один и тот же алгоритм для различных.

Презентация:



Advertisements
Похожие презентации
Статические поля класса Статические поля хранят данные, общие для всех элементов класса. Статическое поле существует в единственном экземпляре для всех.
Advertisements

Лекция 13. Введение в ООП. Часть 4 Красс Александр СПбГУ ИТМО, 2008.
Основы информатики Классы Заикин Олег Сергеевич zaikin.all24.org
Практическое занятие 6. Функции. Большинство языков программирования используют понятия функции и процедуры. C++ формально не поддерживает понятие процедуры,
Преобразования типов В языке C/C++ имеется несколько операций преобразования типов. Они используются в случае, если переменная одного типа должна рассматриваться.
Функции Функция – именованная последовательность описаний и операторов, выполняющая некоторое действие. Может иметь параметры и возвращать значение. Функция.
1 Переопределение операций Макаревич Л. Г.. 2 Зачем нужна перегрузка операций? class Complex { double re; double im; public: Complex(double r=0, double.
Множественное наследование class A {... }; class B {... }; class C : public A, protected B {... }; !!! Спецификатор доступа распространяется только на.
Лекция 10. Введение в ООП. Часть 3 Красс Александр СПбГУ ИТМО, 2008.
Дружественные функции Дружественные функции – это функции, объявленные вне класса, но имеющие доступ к закрытым и защищенным полям данного класса Дружественная.
Наследование Наследование – это отношение является между классами. class Person { string first_name; int birth_year;... } class Student : Person { float.
ОБЪЕКТНО- ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ (ООП) 1.
УКАЗАТЕЛИ. Переменная - это именованная область памяти с заданным типом. [=значение]; int a; //Переменная типа integer с именем a int b=2;// Переменная.
Лекция 2: Описание класса 1. Поля 2. Методы 3. Конструкторы.
Лекция 10 ОбъектыЛекция 10 ОбъектыООП Инкапсуляция Возможность совместного хранения данных и кода для их обработки Наследование Возможность расширять существующие.
Основы информатики Лекция. Массивы. Указатели. Заикин Олег Сергеевич
Полиморфизм Полиморфизм (polymorphism) - последний из трех "китов", на которых держится объектно-ориентированное программирование Слово это можно перевести.
Основы информатики Массивы. Указатели. Заикин Олег Сергеевич
Лекция 4. Введение в С++ Наследование, множественное наследование. Конструкторы, деструкторы. Виртуальные функции.
Перегрузка операторов x = a + b результат 1-й операнд2-й операнд оператор По количеству операндов операторы делятся на: унарные (один операнд) бинарные.
Транксрипт:

Май Перегрузка операторов и функций

Май Перегрузка функций Бывает удобно, чтобы функции, реализующие один и тот же алгоритм для различных типов данных, имели одно и то же имя. Бывает удобно, чтобы функции, реализующие один и тот же алгоритм для различных типов данных, имели одно и то же имя. Если это имя мнемонично, то есть несет нужную информацию, это делает программу более понятной, поскольку для каждого действия требуется помнить только одно имя. Если это имя мнемонично, то есть несет нужную информацию, это делает программу более понятной, поскольку для каждого действия требуется помнить только одно имя. Использование нескольких функций с одним и тем же именем, но с различными типами параметров, называется перегрузкой функций. Использование нескольких функций с одним и тем же именем, но с различными типами параметров, называется перегрузкой функций. Компилятор определяет, какую именно функцию требуется вызвать, по типу передаваемых параметров. Компилятор определяет, какую именно функцию требуется вызвать, по типу передаваемых параметров. Этот процесс называется разрешением перегрузки (перевод английского слова resolution в смысле «уточнение»). Тип возвращаемого функцией значения в разрешении не участвует. Этот процесс называется разрешением перегрузки (перевод английского слова resolution в смысле «уточнение»). Тип возвращаемого функцией значения в разрешении не участвует. Механизм разрешения основан на достаточно сложном наборе правил, смысл которых сводится к тому, чтобы использовать функцию с наиболее подходящими аргументами и выдать сообщение, если такой не найдется. Механизм разрешения основан на достаточно сложном наборе правил, смысл которых сводится к тому, чтобы использовать функцию с наиболее подходящими аргументами и выдать сообщение, если такой не найдется.

Май Пример перегрузки функций Имеется четыре варианта функции, определяющей наибольшее значение: // Возвращает наибольшее из двух целых: int max(int, int); // Возвращает подстроку наибольшей длины: char* max(char*, char*); // Возвращает наибольшее из первого параметра и длины второго: int max (int, char*); // Возвращает наибольшее из второго параметра и длины первого: int max (char*, int);

Май Вызов различных функций При вызове функции max компилятор выбирает соответствующий типу фактических параметров вариант функции (в приведенном примере будут последовательно вызваны все четыре варианта функции). При вызове функции max компилятор выбирает соответствующий типу фактических параметров вариант функции (в приведенном примере будут последовательно вызваны все четыре варианта функции). void f(int a, int b, char* c, char* d){ cout « max (a, b) « max(c, d) « max(a, c) « max(c, b); }

Май Неоднозначности при вызове перегруженных функций Неоднозначность может появиться при: Неоднозначность может появиться при: преобразовании типа; преобразовании типа; использовании параметров-ссылок; использовании параметров-ссылок; использовании аргументов по умолчанию. использовании аргументов по умолчанию.

Май Пример неоднозначности при преобразовании типа #include #include float f(float i){ cout « "function float f(float i)" « endl; return i; } double f(double i){ cout « "function double f(double i)" « endl; return i*2; } int main(){ float x = 10.09; double у = 10.09;' cout « f(x) « endl; // Вызывается f(float) cout « f(y) « endl; // Вызывается f(double) /* cout « f(10) « endl; Неоднозначность - как преобразовать 10: во float или double? */ return 0; }

Май Пример неоднозначности при использовании параметров-ссылок Функции Функции int f(int a, int b); int f(int a, int b); int f(int a, int &b); int f(int a, int &b); Вызов Вызов int a = 5, b=10; int a = 5, b=10; f(a,b); f(a,b); Компилятор не сможет узнать, какая из этих функций вызывается, так как нет синтаксических различий между вызовом функции, которая получает параметр по значению, и вызовом функции, которая получает параметр по ссылке. Компилятор не сможет узнать, какая из этих функций вызывается, так как нет синтаксических различий между вызовом функции, которая получает параметр по значению, и вызовом функции, которая получает параметр по ссылке.

Май Пример неоднозначности при использовании аргументов по умолчанию #include #include int f(int a){return a;} int f(int a, int b=1){return a * b;} int main(){ cout « f(10, 2); // Вызывается f(int, int) /* cout « f(10);Неоднозначность - что вызывается: f(int, int) или f(int) */ return 0; }

Май Правила описания перегруженных функций Перегруженные функции должны находиться в одной области видимости, иначе произойдет сокрытие аналогично одинаковым именам переменных во вложенных блоках. Перегруженные функции должны находиться в одной области видимости, иначе произойдет сокрытие аналогично одинаковым именам переменных во вложенных блоках. Перегруженные функции могут иметь параметры по умолчанию, при этом значения одного и того же параметра в разных функциях должны совпадать. Перегруженные функции могут иметь параметры по умолчанию, при этом значения одного и того же параметра в разных функциях должны совпадать. В различных вариантах перегруженных функций может быть различное количество параметров по умолчанию. В различных вариантах перегруженных функций может быть различное количество параметров по умолчанию. Функции не могут быть перегружены, если описание их параметров отличается только модификатором const или использованием ссылки (например, int и const int или int и int&). Функции не могут быть перегружены, если описание их параметров отличается только модификатором const или использованием ссылки (например, int и const int или int и int&).

Май Перегрузка операторов Третье поколение ЯП – языки, предоставляющие программисту средства определения абстрактных типов данных Третье поколение ЯП – языки, предоставляющие программисту средства определения абстрактных типов данных С++, C#, Java С++, C#, Java Типы понимаются как множества с операциями. Типы понимаются как множества с операциями. Хотелось бы, что бы одинаковые по смыслу операции обозначались бы одинаково не зависимо от того используется встроенный тип данных или тип данных определенный пользователем. Хотелось бы, что бы одинаковые по смыслу операции обозначались бы одинаково не зависимо от того используется встроенный тип данных или тип данных определенный пользователем. В этом случае операции, обозначаемые одним и тем же символом будут приводить к выполнению совершенно различных действий в зависимости от контекста. В этом случае операции, обозначаемые одним и тем же символом будут приводить к выполнению совершенно различных действий в зависимости от контекста.

Май Перегруженный оператор + int a,b; double c,d; char *e,*f; a = a + b; с = с + d; e = f + a; string s1= abc, s2 = def, s3; s3 = s1 + s2; // s3 = abcdef;

Май Правила перегрузки операций Перегрузка операций осуществляется с помощью функций специального вида (функций-операций) Перегрузка операций осуществляется с помощью функций специального вида (функций-операций) Функция-операция содержит ключевое слово operator, за которым следует знак переопределяемой операции: Функция-операция содержит ключевое слово operator, за которым следует знак переопределяемой операции: тип operator операция ( список параметров) { тело функции } тип operator операция ( список параметров) { тело функции } при перегрузке операций сохраняются количество аргументов, приоритеты операций и правила ассоциации (справа налево или слева направо), используемые в стандартных тинах данных; при перегрузке операций сохраняются количество аргументов, приоритеты операций и правила ассоциации (справа налево или слева направо), используемые в стандартных тинах данных; для стандартных типов данных переопределять операции нельзя; для стандартных типов данных переопределять операции нельзя; функции-операции не могут иметь аргументов по умолчанию; функции-операции не могут иметь аргументов по умолчанию; функции-операции наследуются (за исключением =); функции-операции наследуются (за исключением =); функции-операции не могут определяться как static. функции-операции не могут определяться как static. Функцию-операцию можно определить тремя способами: Функцию-операцию можно определить тремя способами: как метод класса, как метод класса, дружественная функция класса, дружественная функция класса, обычная функция. обычная функция.

Май Переопределение операции с помощью метода класса class Square{ private: int x,y; int size; public: Square(){x = 0; y = 0; size = 10;} print(){printf( x=%i, y= %i, size=%i\n,x,y,size);} Square &operator++() {size++; return *this;} …};… Square s; (++s).print();…

Май Переопределение операции с помощью обычной функции class Square{ private: int x,y; public: int size; Square(){x = 0; y = 0; size = 10;} print(){printf( x=%i, y= %i, size=%i\n,x,y,size);} …}; Square &operator++(Square &s) {s.size++; return s;} … Square s; (++s).print();…

Май Переопределение операции с помощью функции - друга class Square{ private: int x,y; int size; public: Square(){x = 0; y = 0; size = 10;} print(){printf( x=%i, y= %i, size=%i\n,x,y,size);} friend Square &operator++(Square &s); …}; Square &operator++(Square &s) {s.size++; return s;} Square s; (++s).print();…

Май Унарная постфиксная операция Операции постфиксного инкремента и декремента должны иметь первый параметр типа int. Операции постфиксного инкремента и декремента должны иметь первый параметр типа int. Он используется только для того, чтобы отличить их от префиксной формы: Он используется только для того, чтобы отличить их от префиксной формы: class Square{ private: int x,y; int size; public: Square(){x = 0; y = 0; size = 10;} print(){printf( x=%i, y= %i, size=%i\n,x,y,size);} Square &operator++(int) {size++; return *this;} …};… Square s; s++;s.print();…

Май Перегрузка бинарных операций Бинарная функция-операция, определяемая внутри класса, должна быть представлена с помощью нестатического метода с параметрами, при этом вызвавший ее объект считается первым операндом. Бинарная функция-операция, определяемая внутри класса, должна быть представлена с помощью нестатического метода с параметрами, при этом вызвавший ее объект считается первым операндом. Если функция определяется вне класса, она должна иметь два параметра типа класса. Если функция определяется вне класса, она должна иметь два параметра типа класса.

Май class Square{ private: int x,y; int size; public: Square(){x = 0; y = 0; size = 10;} print(){printf( x=%i, y= %i, size=%i\n,x,y,size);} bool operator>(const Square &s) { if(size >s.size) return true; else return false; }…};… Square s1,s2; if(s1 > s2) printf(s1 больше!); …

Май class Square{ private: int x,y; int size; public: Square(){x = 0; y = 0; size = 10;} print(){printf( x=%i, y= %i, size=%i\n,x,y,size);} friend bool operator>(const Square &s1, const Square &s2); …}; bool operator>(const Square &s1, const Square &s2) { if(s1. size >s2.size) return true; else return false; }… Square s1,s2; if(s1 > s2) printf(s1 больше!); …

Май Перегрузка операции присваивания Операция присваивания определена в любом классе по умолчанию как поэлементное копирование. Операция присваивания определена в любом классе по умолчанию как поэлементное копирование. Она вызывается каждый раз, когда одному существующему объекту присваивается значение другого. Она вызывается каждый раз, когда одному существующему объекту присваивается значение другого. Если класс содержит поля, память под которые выделяется динамически, необходимо определить собственную операцию присваивания. Если класс содержит поля, память под которые выделяется динамически, необходимо определить собственную операцию присваивания. Чтобы сохранить семантику присваивания, операция- функция должна возвращать ссылку на объект, для которого она вызвана, и принимать в качестве параметра единственный аргумент ссылку на присваиваемый объект. Чтобы сохранить семантику присваивания, операция- функция должна возвращать ссылку на объект, для которого она вызвана, и принимать в качестве параметра единственный аргумент ссылку на присваиваемый объект.

Май Класс «слушатель» без перегрузки оператора присваивания class SL{ int group; char *fio; public: SL(int group,char *fio){ this->group = group; this->fio = new char[strlen(fio)+1)]; strcpy(this->fio,fio);}SL(){ group = 0; fio = NULL; }~SL(){ if(fio!= NULL) delete[] fio; if(fio!= NULL) delete[] fio;} void ChangeName(char *newName){ if(fio!= NULL)delete[] fio; fio = new char[strlen(newName)+1)]; strcpy(fio,newName);}};

Май Результат операции присваивания group = 7313 fio = адрес 1 slush1 Иванов И.И. group = 7313 fio = адрес 1 slush2 SL slush1(7313,Иванов И.И.); SL shush2; shush2 = slush1;

Май Результат вызова «ChangeName» group = 7313 fio = адрес 1 slush1 Иванов И.И. group = 7313 fio = адрес 1 slush2 shush1.ChangeName(Петров И.И.); Петров И.И.

Май Класс «слушатель» с перегруженным оператором присваивания class SL{ int group; char *fio; public: SL(int group,char *fio){ this->group = group; this->fio = new char[strlen(fio)+1)]; strcpy(this->fio,fio);}… SL &operator=(SL &sl){ group = sl.group; if(fio != NULL) delete[] fio; fio = new char[strlen(sl.fio)+1)]; strcpy(fio, sl.fio); return *this; }};

Май Результат операции присваивания group = 7313 fio = адрес 1 slush1 Иванов И.И. group = 7313 fio = адрес 2 slush2 SL slush1(7313,Иванов И.И.); SL shush2; shush2 = slush1; \\ slush2.operator=(slush1) Иванов И.И.

Май Конструктор копирования Конструктор копирования специальный вид конструктора, получающего в качестве единственного параметра ссылку на объект этого же класса. Конструктор копирования специальный вид конструктора, получающего в качестве единственного параметра ссылку на объект этого же класса. Т: :Т(const T&) {... /* Тело конструктора */ } Т: :Т(const T&) {... /* Тело конструктора */ } где Т имя класса. где Т имя класса. Этот конструктор вызывается в тех случаях, когда новый объект создается путем копирования существующего: Этот конструктор вызывается в тех случаях, когда новый объект создается путем копирования существующего: при описании нового объекта с инициализацией другим объектом; при описании нового объекта с инициализацией другим объектом; при передаче объекта в функцию по значению; при передаче объекта в функцию по значению; при возврате объекта из функции. при возврате объекта из функции. Если программист не указал ни одного конструктора копирования, компилятор создает его автоматически. Такой конструктор выполняет побайтное копирование полей. Если программист не указал ни одного конструктора копирования, компилятор создает его автоматически. Такой конструктор выполняет побайтное копирование полей. Если класс содержит указатели или ссылки, это, скорее всего, будет неправильным, поскольку и копия, и оригинал будут указывать на одну и ту же область памяти. Если класс содержит указатели или ссылки, это, скорее всего, будет неправильным, поскольку и копия, и оригинал будут указывать на одну и ту же область памяти.

Май Пример конструктора копирования class SL{ int group; char *fio; public: SL(int group,char *fio){ this->group = group; this->fio = new char[strlen(fio)+1)]; strcpy(this->fio,fio);}… SL(const SL &sl){ group = sl.group; fio = new char[strlen(sl.fio)+1)]; strcpy(fio, sl.fio); }};

Май Перегрузка операции приведения типа Можно определить функции-операции, которые будут осуществлять преобразование объекта класса к другому типу. Можно определить функции-операции, которые будут осуществлять преобразование объекта класса к другому типу. Формат: Формат: operator имя_нового_типа() operator имя_нового_типа() Тип возвращаемого значения и параметры указывать не требуется. Тип возвращаемого значения и параметры указывать не требуется.

Май Использование перегрузки переопределения типа class SL{ int group; char *fio; public:… operator char*(){ static char str[256]; sprintf(str,%s (группа %i),fio,group); return str; };… SL slush1(7313,Иванов И.И.); printf(%s\n, (char *)shush1); … результат – Иванов И.И. (группа 7313)

Май Перегрузка операции индексирования Операция индексирования [ ] обычно перегружается, когда тип класса представляет множество значений, для которого индексирование имеет смысл. Операция индексирования [ ] обычно перегружается, когда тип класса представляет множество значений, для которого индексирование имеет смысл. Операция индексирования должна возвращать ссылку на элемент, содержащийся в множестве. Операция индексирования должна возвращать ссылку на элемент, содержащийся в множестве.

Май Класс слушатель class SL{ public: int NN;// личный номер int group;// номер группы char fio[100];// ФИО int high;// рост int weight;// вес … // разные другие данные };

Май Класс «слушатели» Хранит указатели на объекты класса слушатель в односвязном линейном списке Хранит указатели на объекты класса слушатель в односвязном линейном списке class SLList{ struct EL{ SL *slush; EL *next; }; EL *first; public: SLList(){first = NULL} ~SLList(); void Add(SL *sl); void Delete(); SL *operator[](int nn); SL *operator[](char *fio); };

Май Получение слушателя по личному номеру SL *SLList::operator[](int nn){ SL *ret = first; while(ret != NULL){ if(ret->NN == nn) return ret; ret = ret->next; } return NULL; } SLList spisok; // … многократные вызовы spisok.Add(…); // потребовалось найти слушателя с личным номером 123 SL *slush123 = spisok[123]; if(slush123 == NULL){ // действия если такого слушателя не нашлось }

Май Получение слушателя по ФИО SL *SLList::operator[](char *fio){ SL *ret = first; while(ret != NULL){ if(!strcmp(ret->fio,fio)) return ret; ret = ret->next; } return NULL; } SLList spisok; // … многократные вызовы spisok.Add(…); // потребовалось найти слушателя «Иванов И.И.» SL *slush123 = spisok[«Иванов И.И.»]; if(slush123 == NULL){ // действия если такого слушателя не нашлось }

Май Статические элементы класса С помощью модификатора static можно описать статические поля и методы класса. С помощью модификатора static можно описать статические поля и методы класса. Их можно рассматривать как глобальные переменные или функции, доступные только в пределах области класса. Их можно рассматривать как глобальные переменные или функции, доступные только в пределах области класса.

Май Статические поля Статические поля применяются для хранения данных, общих для всех объектов класса, например, количества объектов или ссылки на разделяемый всеми объектами ресурс. Статические поля применяются для хранения данных, общих для всех объектов класса, например, количества объектов или ссылки на разделяемый всеми объектами ресурс. Эти поля существуют для всех объектов класса в единственном экземпляре, то есть не дублируются. Эти поля существуют для всех объектов класса в единственном экземпляре, то есть не дублируются. x,y class A{ static int N; int x,y; }; A a,b,c,d; x,y N a b c d

Май Особенности статических полей (1) Память под статическое поле выделяется один раз при его инициализации независимо от числа созданных объектов (и даже при их отсутствии) и инициализируется с помощью операции доступа к области действия, а не операции выбора (определение должно быть записано вне функций) Память под статическое поле выделяется один раз при его инициализации независимо от числа созданных объектов (и даже при их отсутствии) и инициализируется с помощью операции доступа к области действия, а не операции выбора (определение должно быть записано вне функций) class A{ public: static int count; }; int A::count = 10; //Пример инициализации произвольным значением // По умолчанию инициализируется нулем

Май Особенности статических полей (2) Статические поля доступны как через имя класса, так и через имя объекта Статические поля доступны как через имя класса, так и через имя объекта А *а, b; cout « A::count « a->count « b.count; // Будет выведено одно и то же

Май Особенности статических полей (3) На статические поля распространяется действие спецификаторов доступа, поэтому статические поля, описанные как private, нельзя изменить с помощью операции доступа к области действия. На статические поля распространяется действие спецификаторов доступа, поэтому статические поля, описанные как private, нельзя изменить с помощью операции доступа к области действия. class B{ static int count1; public: static int count2; void changeCount1(int newValue){ count1 = newValue;} }; B::count1 = 10; B::count2 = 20; main(){… B::count1++;// ошибка! B::count2++;// нормально B var; var. changeCount1(50);// нормально …}

Май Особенности статических полей (4) Память, занимаемая статическим полем, не учитывается при определении размера объекта с помощью операции sizeof. Память, занимаемая статическим полем, не учитывается при определении размера объекта с помощью операции sizeof. class B{ public: static int count; int tmp; };… int size = sizeof(B);// size = 4

Май Статические методы (1) Статические методы предназначены для выполнения действий не зависящих от конкретного объекта. Статические методы предназначены для выполнения действий не зависящих от конкретного объекта. class MyClass{ …public: static char *GetClassName(){ return MyClass; }};

Май Статические методы (2) Могут обращаться непосредственно только к статическим полям и вызывать только другие статические методы класса, потому что им не передается скрытый указатель this. Могут обращаться непосредственно только к статическим полям и вызывать только другие статические методы класса, потому что им не передается скрытый указатель this. class TestStaticMetod{ int field1; static int field2; public:… void SimpleMetod(); static void StaticMetod1(); static void StaticMetod2(){ field2 = 100;// правильно field1 = 200;// ошибка SimpleMetod();// ошибка StaticMetod2();// правильно }}

Май Статические методы (3) Обращение к статическим методам производится так же, как к статическим полям либо через имя класса, либо, если хотя бы один объект класса уже создан, через имя объекта. Обращение к статическим методам производится так же, как к статическим полям либо через имя класса, либо, если хотя бы один объект класса уже создан, через имя объекта. class TestStaticMetod{ int field1; static int field2; public:… void SimpleMetod(); static void StaticMetod1(); static void StaticMetod2(); }main(){… TestStaticMetod a, *b; …a.StaticMetod1();b->StaticMetod1();TestStaticMetod::StaticMetod1();}