Язык программирования Си++ (1119468), страница 4
Текст из файла (страница 4)
Метод класса имеет право доступа к закрытой частиобъявления класса2. Метод класса находится в области видимости класса3. Метод должен вызываться только для объекта класса(имеется неявно присутствующий указатель this)• Статические методы не имеют указателя this и могутвызываться, когда объектов у класса не существует• Функции-друзья, обладая доступом к закрытой частиобъявления класса, не обязаны находиться в области70видимости этого классаФункции-друзья классов• Иллюстрация различий в определении и использованииметодов класса и функций-друзей:class X { int a; public: ...friend void friend_f (X*, int);void member_m (int);}void friend_f (X * p, int i) { p -> a = i; }void X::member_m (int i) { a = i; }void f (){ X obj;friend_f (& obj, 10);obj.member_m (10);}71Функции-друзья классов• Преимущества использования функций-друзей:1.
Функции-друзья повышают эффективность программ,позволяя отказаться от использования функций доступа кзакрытым членам класса2. Объявление функции другом сразу нескольких классовпозволяет упростить интерфейсы сразу всех этих классов3. Функция-друг не накладывает ограничений на спискисобственных параметров и не требует делать главныйпараметр (используемый объект) первым в спискепараметров функции4. Функция-друг допускает преобразования главногопараметра (используемого объекта), а методы класса72этого не допускаютФункции-друзья классов• Если другом объявляется перегруженная функция,только она из одноимённых функций является другом• Дружба не передаётся по наследству производнымклассам: она не обладает транзитивностью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 ++; } };// нет доступа к закрытому полю ‘a’73Перегрузка операций• Перегрузка может производиться для операций спараметрами встроенных типов:class complex { double re, im; /* ...
*/public: complex (double r = 0, double i = 0) { re = r; im = i; }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;}• Можно использовать операторыcomplex y = 2, z; double d = 5.3;z = y * d; // вызывается функция operator*(y, d)74Перегрузка операций• Ввиду отсутствия варианта перегрузки умножения спервым параметром, имеющим тип double, остаётсяневерным операторz = d * y; // Ошибка• Следует определить ещё одну дружественную классуcomplex функцию:complex operator * (double a, const complex &b) {complex temp (b.re * a, b.im * a);return temp;}75Перегрузка операций• Имея такие определения, можно написать программу:complex x (1, 2), y (5,8), z; const complex w (1, 3); double d = 7.5;z = x + y; /* x.operator + (y) */z = x + w; // const внутри скобокz = z + d; /* z.operator + (complex (d)) */ z = w + x; // const после скобокz = d + x;// ошибка: вызов d.operator+ (x), но в классе double// нет операции сложения с комплексными числами• Методы класса в качестве своего первого параметравсегда имеют параметр, имеющий тип этого класса• Функции, не являющиеся методами классов, свободныот этого требования• Перегрузка операций членами класса обычноиспользуется, когда оба операнда относятся к этому76классуПерегрузка операций• Перегрузка операции ‘+’ с помощью друга класса:class complex { double re, im; /* ...
*/public: complex (double r = 0, double i = 0) { re = r; im = i; }friend complex operator+ (const complex& a, const complex& b){ complex temp (re + a.re, im + a.im); return temp; }};complex x (1, 2), y (5,8), z; const complex w (1, 3); double d = 7.5;z = x + y; // operator + (x, y)z = z + d; // operator + (z, complex (d))z = d + x; // operator + (complex (d), x), нет ошибок• Функции-друзья лучше использовать в тех случаях,кода в операциях участвуют операнды разных типовПерегрузка операций• Допускается не более одного пользовательскогопреобразования для обработки одного вызова для одногопараметраclass X { public: operator int (); ...
}; // можно преобразовать в intclass Y { public: operator X (); ... }; // можно преобразовать в Xvoid f () { Y a; int b;b = a; } // неправильно: b = a.operator X ().operator int ();• Двойной шаг по пункту г алгоритма запрещён• Если написать явные шаги преобразования типа, ошибки небудет: цепочки явных преобразований могут быть сколь угоднодлинными• Присваивание подчиняется тем же правилам, что и другиеперегруженные функции78Операция индексирования• Индексация ‘[]’ есть бинарная операция, её перегрузкавыполняется только нестатическим методом класса, у которогонеявный параметр – это сам объект, к которому применяетсяоперация, а явный параметр – значение индексаclass string { char * p; int size; /* … */public: char & operator [] (int i);{ if (i < 0 || i >= size) { cerr << “string:” << i << endl; exit (1); }return p [i];}} s (“Системы программирования”);char c = s [3];// эквивалентно c = s.operator [] (3); => c == ‘т’• Возвращаемое значение в виде ссылки позволяет использоватьиндексацию в присваивании и справа и слева79Перегрузка унарных операций• Унарные операции перегружаются методом без параметров(точнее – с одним неявным параметром)class complex { double re, im;public: complex (double r = 0, double i = 0) {re = r; im = i;}const complex operator– () const // –x = y x = –ComplexConst1{ complex temp (– re, – im); return temp; }};complex x (1, 2), z;z = – x; // z.re = – 1, z.im = – 2z = – 2; // здесь нет никакой перегрузки унарного минуса:// работает присваивание значения выражения –280Перегрузка унарных операций• Унарные операции перегружаются функцией-другом с однимпараметром (допустимы и обычные функции)class complex { double re, im;public: complex (double r = 0, double i = 0) {re = r; im = i;}friend complex operator– (const complex & a);};complex operator– (const complex & a){ complex temp (– a.re, – a.im); return temp; }complex x (1, 2), z;z = – x; // z.re = – 1, z.im = – 2: результат тот же81Особенности операций ‘++’ и ‘--’• При перегрузке нужно указывать, какой именно вариантперегружается (префиксный или постфиксный)• Для двух операций языка определён специальный синтаксис:++a ≡ a.operator++ ()--a ≡ a.operator-- ()a++ ≡ a.operator++ (0)a-- ≡ a.operator-- (0)• Для указания на постфиксную форму используется фиктивныйаргумент c типом int: operator++ (int)• Как и все остальные унарные арифметические и логическиеоперации (они все префиксные), префиксная операцияопределяется без фиктивного аргумента: operator++()• Фиктивный аргумент используется только для “необычных”(единственных в своем роде) постфиксных операций ‘++’ и ‘--’Особенности операций ‘++’ и ‘--’• Префиксные операции обычно возвращают ссылки на объекты,а постфиксные – копии этих объектов• Префиксная операция-функция получает ссылку нанеконстантный объект, копия которого внутри функции несоздаётся, а возвращает ссылку на константу, чтобы запретитьоперации вида ++++ z или ++ z = …• Постфиксная операция-функция возвращает константноезначение, чтобы запретить операции вида z ++++ или z ++ = …;ссылка не возвращается: нужна копия неизменённого объектаclass complex { double re, im; /* … */const complex & operator++ () { ++ re; return * this; }const complex operator++ (int pusto){ complex temp = * this; re ++; return temp; }83};Особенности операций ‘++’ и ‘--’class complex { double re, im; /* … */public: complex (double r = 0, double i = 0) {re = r; im = i;}const complex & operator++ () { ++ re; return * this; }const complex operator++ (int pusto){ complex temp = * this; re ++; return temp; }};complex x (1, 2), y, z;z = ++ x;z = x ++;++ ++ x;y = (x + y) ++;// z.re = 2, z.im = 2, x.re = 2, z.im = 2// z.re = 2, z.im = 2, x.re = 3, z.im = 2// ОШИБКА: возвращается не адресное значение// ОШИБКА: сложение возвращает// не адресное значение84Перегрузка операций• Виды определений перегрузки операций:class X {// члены класса (имеют неявный параметр this)X operator ++ (int); // постфиксная унарная операция увеличенияX & operator ++(); // префиксная унарная операция увеличенияX * operator &(); // префиксная унарная операция взятия адреса (‘&’)X operator &(X); // бинарная операция логического И (‘&’)X operator & (X, X); // ошибка: заданы три операнда операции ‘&’(); // ошибка: нет унарной операции деления (‘/’)X operator /};// функции, не являющиеся членами классаX operator -- (X&, int); // постфиксная унарная операция уменьшенияX &operator -(X&); // префиксная унарная операция уменьшенияX operator (X); // префиксная унарная операция изменения знакаX operator (X, X); // бинарная операция вычитания (‘–’)(); // ошибка: не заданы операнды операции ‘–’X operator X operator - (X, X, X); // ошибка: заданы три операнда операции ‘–’(X); // ошибка: нет унарной операции остатка (‘%’)X operator %Виды отношений междуклассами• Классы могут рассматриваться как автономные абстрактныесущности, однако• в безусловном большинстве существующихпрограмм классы взаимосвязаны• Разновидности взаимосвязей (в том числе – иерархий):•••••Ассоциация классовАгрегация классовИспользование одним классом другого классаИнстанцирование (наполнение) классаНаследование одним классом свойств другогокласса86Основные понятия ER-модели• Сущность (Entity) – абстракция, полученная на основесходных объектов, информация о которых должнасохраняться и быть доступной• В диаграммах ER-модели сущность представляется в видепрямоугольника, содержащего имя сущностиАЭРОПОРТнапример, Шереметьево, Хитроу• Имя сущности – это имя типа (класса), а не некоторогоконкретного экземпляра этого типа• Атрибут – именованная характеристика сущности• Ключ сущности – совокупность атрибутов, однозначноопределяющая конкретный экземпляр87Основные понятия ER-модели• Связь (Relationship) – графически изображаемаяассоциация, устанавливаемая между сущностями, связь –это типовое понятие, все экземпляры связываемыхсущностей подчиняются единым правилам связыванияПокупательпростосвязьЗаказПокупательсвязьN“много-к-одному”ЗаказПокупательсвязь 1:N“много-к-одному”ЗаказПокупатель1связь“один-к-одному”Заказ• Модальность связи – “должен” или “может”88Взаимодействие и иерархияклассов.