Лекции (7) (Презентации лекций (PDF))
Описание файла
Файл "Лекции (7)" внутри архива находится в папке "Презентации лекций (PDF)". PDF-файл из архива "Презентации лекций (PDF)", который расположен в категории "". Всё это находится в предмете "практикум (прикладное программное обеспечение и системы программирования)" из 4 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст из PDF
Друзья классаДруг класса – это функция, не являющаяся членом этого класса,но имеющая доступ к его private и protected членам.Своих друзей класс объявляет сам в любой зоне описания классас помощью служебного слова friend.Функция-друг может быть описана внутри класса.Если функций, имена которых совпадают с объявленной в классефункцией-другом, несколько, то другом считается только та, укоторой в точности совпадает прототип.Другом класса может быть:- обычная функция:- функция-член другого класса:- весь класс:friend void f (...);friend void Y::f (..);friend class Y;1Свойства друзей классаДружба не обладает ни наследуемостью, ни транзитивностью.Примеры:class A {friend class B;int a;};class B {friend class C;};class C {void f (A* p) {p -> a++; // ошибка, нет доступа к закрытым членам класса А}};class D: public B {void f (A* p) {p -> a++; // ошибка, нет доступа к закрытым членам класса А}};2Использование функций - друзей классаclass X {int a;friend void fff ( X *, int); // здесь нет this !public:void mmm (int);};void fff ( X * p, int i) {p -> a = i;}void X::mmm (int i) {a = i;}void f () {X obj;fff (&obj, 10);obj.mmm (10);}3Преимущества использования друзей класса1.
Эффективность реализации ( можнообходить ограничения доступа,предназначенные для обычныхпользователей).2. Функция-друг нескольких классов позволяетупростить интерфейс этих классов.3. Функция-друг допускает преобразованиесвоего первого параметра-объекта, а методкласса - нет.4Перегрузка операций• Для перегрузки встроенных операций С++используется ключевое слово operator.• Перегружать операцию можно с помощью- метода класса,- внешней функции, в частности, функции-друга(что менее эффективно).• Нельзя перегружать:‘.’ , ‘::’ , ‘?:’ , ‘.*’ , sizeof, и typeid !!!5Пример 1.class complex {double re, im;public:complex (double r = 0, double i = 0) {re = r;im = i;}complex operator+ (const complex & a) {complex temp (re + a.re, im + a.im);return temp;} ...// operator double () { return re; } – функция преобразования};int main () {complex x (1, 2), y (5, 8), z;double t = 7.5;z = x + y; // O.K.
– x.operator+ (y);z = z + t; // O.K. – z.operator+ (complex (t)); если есть ф-я преобр., то// неоднозначность: '+' - double или перегруженныйz = t + x; // Er.! – т.к. первый операнд по умолчанию – типа complex.6}Пример 2.class complex {double re, im;public:complex (double r = 0, double i = 0) {re = r;m = i;}friend complex operator+ (const complex & a, const complex & b);...};complex operator+ (const complex & a, const complex & b) {complex temp (a.re + b.re, a.im + b.im);return temp;}int main () {complex x (1, 2), y (5, 8), z;double t = 7.5;z = x + y; // O.K. – operator+ (x, y);z = z + t; // O.K.
– operator+ (z, complex (t));z = t + x; // O.K. – operator+ (complex (t), x);7}Пример 3.class complex {double re, im;public:friend complex operator * (const complex & a, double b);...};complex operator * (const complex & a, double b) {complex temp (a.re * b, a.im * b);return temp;}int main () {complex x (1, 2), z;double t = 7.5;z = x * t;// O.K. – operator* (x, t);z = t * x;// Er.! т.к. нет функции преобразования x --> double, но// если бы была, была бы неоднозначность:// * - из double или из complex}В таких случаях обычно определяют еще одного друга с прототипом:complex operator * (double b, const complex & a);8Замечания- n-местные операции перегружаютсяa) методом с (n-1) параметром,b) внешней функцией с n параметрами;- в любом случае сохраняется приоритет,ассоциативность и местность операций;- операции= , [ ] , ( ) и ->можно перегрузить только нестатическимиметодами класса, что гарантирует, что первымоперандом будет сам объект, к которомуоперация применяется;9Особенности перегрузки операций ++ и -complex x;префиксная ++:++ x; ~ x.operator ++ ();complex & operator ++ () {re = re + 1;im = im + 1;return *this;}постфиксная ++:x ++; ~ x.operator ++ (0);complex operator ++ (int) {complex c = * this;re = re + 1;im = im + 1;return c;}10Перегрузка операции Операцию перегружают методом класса, объекты которого играютроль «умных» указателей на объекты другого класса.Операцию можно считать постфиксной унарной, посколькупреобразование объекта класса в указатель не зависит от конкретногополя, на которое он указывает.Пример:struct T { int f;};class Tptr {T* pt;public:Tptr () { pt = new T; }T* operator () {return pt;}~Tptr () { delete pt; }};Метод operator () обязан возвращать либо указатель, либо объекткласса, для которого также перегружена операция .
Последним вцепочке перегруженных операций должен быть метод,возвращающий указатель на объект некоторого класса.11Пример перегрузки операции «( )» и операции вывода «<<»class Matrix {double M [ 3 ] [ 3 ];public:Matrix ();double & operator ( ) (int i, int j) const {return M [ i ] [ j ];}friend ostream & operator << (ostream & s, const Matrix & a){for (int i = 0; i < 3 ; i ++) {for (int j = 0; j < 3; j ++)s << a (i, j) << ' ';s << endl;}return s;}};12Перегрузка функцийО перегрузке можно говорить только для функций из однойобласти видимости!Алгоритм поиска и выбора функции:1.
Выбираются только те перегруженные (одноименные)функции, для которых фактические параметрысоответствуют формальным по количеству и типу(приводятся с помощью каких-либо преобразований).2. Для каждого параметра функции (отдельно и по очереди)строится множество функций, оптимальноотождествляемых по этому параметру (best matching).3. Находится пересечение этих множеств:- если это ровно одна функция – она и являетсяискомой,- если множество пусто или содержит более однойфункции, генерируется сообщение об ошибке.13Пример 1.class X { public: X(int);...};class Y {<нет конструктора с параметром типа int>...};void f (X, int);// 1 пар. - ‘+’ 2 пар.
- ‘+’void f (X, double); // 1 пар. - ‘+’ 2 пар. - ‘-‘void f (Y, double); //отбрасывается на 1-м шагеvoid g () {... f (1,1); ...}Т.к. в пересечении множеств, построенных для каждогопараметра, одна функция f (X, int) – вызов разрешим.14Пример 2.struct X { X (int);...};void f (X, int); // 1 пар. - ‘-’ 2 пар. - ‘+’void f (int, X); // 1 пар. - ‘+’ 2 пар. - ‘-‘void g () {... f (1,1); ...}Т.к. пересечение множеств, построенныхдля каждого параметра, пусто – вызовнеразрешим.15Пример 3.void f (char);void f (double);void g () {...
f (1); ...} // ?Не всегда просто выполнить шаг 2алгоритма, поэтому стандартом языка С++закреплены правила сопоставленияформальных и фактических параметров привыборе одной из перегруженных функций.16Правила для шага 2алгоритма выбора перегруженной функцииа) Точное отождествление.б) Отождествление при помощи расширений.в) Отождествление с помощью стандартныхпреобразований.г) Отождествление с помощьюпреобразований, определенныхпользователем.д) Отождествление по ... .17а) Точное отождествление.- точное совпадение,- совпадение с точностью до typedef,- тривиальные преобразования:T[ ] <--> T *,T <--> T&,T --> const T, // в одну сторону!T(...) <--> (T*)(...) .Пример:void f (float);void f (double);void f (int);void g () {...f (1.0);f (1.0F);f (1);// f (double)// f (float)// f (int);...}18б)Отождествление при помощи расширений.- Целочисленные расширения:char, short (signed и unsigned), enum, bool --> int ( unsigned int,если не все значения могут быть представлены типом int –тип unsigned short не всегда помещается в int);- Вещественное расширение:float --> doubleПример:void f (int);void f (double);void g () {short aa = 1;float ff = 1.0;f (ff);f (aa);}Неоднозначности нет, хотя// f (double)// f (int)short -> int & double,float -> int & double.19в)Отождествление с помощью стандартныхпреобразований.- Все оставшиеся стандартные целочисленные и вещественныепреобразования, которые могут выполняться неявно, а такжепреобразование объекта производного класса к объектуоднозначного доступного базового класса.- Преобразования указателей:0 --> любой указатель,любой указатель –> void*,derived* --> base* для однозначного доступногобазового класса;Пример:void f (char);void f (double);void g () { ...
f (0);}// неоднозначность, т.к.// преобр. int --> char и// int --> double равноправны20г)Отождествление с помощью пользовательскихпреобразований.- С помощью конструкторов преобразования.- С помощью функций преобразования.Пример:struct S {S (long);operator int ();};void f (long);void f (char*);// long --> S// S --> intvoid g (S);void g (char*);...void h (const S&);void h (char*);void ex (S &a) {f (a); // O.K.