|
Программирование >> Унарные и бинарные операторы
НО, и затем весь объект tmp возвращается во внепший мир. Казалось бы, все попятно и просто. 11а самом же деле, оператор + для класса complex разрушает все наши представления о доступе к секретным данным объектов. Раньше мы думали, что к области private имеют доступ толькособстве1Н1ые ()ункции объекта, которому и принадлежит .эта область. По взгляните еще раз на операторную функцию для оператора сложения: complex operator+(complex а){ complex tmp; tmp.re=re + a.re; tmp.im-im + a.im; return tmp: В этой функции re и im - действительная и мнимая части текущего объекта, на который указывает this. А вот tmp.re и а.re - действительные части чу-аоа: объектов! Причем доступ к ним идет самым непосредствен ним образом, без всяких собственных функций! Разгадка в том, что С++ считает все объекты одного класса близкими родственниками, разрешая любому такому объекту иметь доступ ко всем закрытым частям Другах родственных объектов. Вот почему наш оператор работает! Ио если бы мы посмели нанисать а.re внутри функции mainO или в любой функции любого объекта, не принадлежащего классу matrix, компилятор вьщал бы сообщение об онн1бке. Задача 9.2. Перепишите конструктор копии из раз-Дела Конструктор копии главы 8 с учетом того, что объектам одного класса разрешен доступ к тайным частям друг друга. Где еще мы использовали собственные функции объекта вместо непосредственного доступа к нему? Листинг 9.7 {продолжение private: double re; double im: Прежде всего обратим внимание па необычный вид конструкторов (всего их три). Как и раньше, в круглых скобках показаны параметры, а после двоеточия перечислены данные и (в скобках) параметры, на эти данные влияющие. Следуюп1ая запись означает, что реальная часть комплексного числа re станет после вызова конструктора равной г, а мнимая im - параметру i: complex(double г. double i) : re(r). im(i) {} Так;1я .запись часто встречается в нрофаммах на С++ и ее нужно знать, хотя старый способ записи конструктора как обычной функции без возвращаемого значения ничуть не хуже: complexCdouble г. double 1){ ге-г: im=i; Всего в HanicM классе conplex три конструктора: первый устанавливает реальную и мнимую части комплексного числа, второй - TOjn.KO реальную, а мнимую полагает по умолчанию равной пулю, третий конструктор совсем не имеет параметров и устанавливает в ноль обе части комплексного числа. Разобравшись с конструкторами, обратимся к операторной функции для сложения. В ней создается временный объект tmp, причем при его создании вызывается конструктор но умолчанию, обнуляюпцш реальную и мнимую части. Далее к реальной части tmp прибавляется реальная часть текущего объекта re и реальная часть объекта а.re, передаваемого в качестве аргумента конструктору. Мнимая часть получас-гся апалогпч- Поняв родственные отношения объектов одного класса, вернемся к оператору сложения и проверим его н деле . Программа, показанная в листинге 9.8, использует включаемый файл complexLh, в котором содержится определение класса complex из листинга 9.7. Листинг 9.8 #1nclude <iostream> using namespace std: #1nclude complexl.h void outccomplex a){ cout a.getre() . a.getimO endl: int main(){ complex a.b(3.2).c(5.4).d: c=b+20: c=b.operator+(20): out(c): 23.2 d=a+b+c: d=(a.operator+(b)).operator+(с): out(d): 26.4 В этой программе результаты сложения вьшодит на экран функция outO. Поскольку она не принадлежит классу complex, в ней непосредственный доступ к области private объектов этого класса невозможен. Потому и приходится использовать собственные функции getre() и getim(). В программе показаны операции сложения с использованием операторов в привычной записи и (в комментариях) в виде операторных функций. Заметим, что оператор сложения независимо от определения всегда выполняется слева направо, то есть к объекту а прибавляется b и к новому объекту прибавляется с. Любопытно проследить за тем, как прибавляется число 20 к объекту Ь. Поскольку оператор - это всего лишь иначе записанная собственная функция, следует ожидать, что для передачи но значению будет использован конструктор копии. Но так было бы при передаче объекта типа сшр1 ex. У нас же передается цeJюe число. Поэтому вызывается один из конструкторов, который преобразует целое число 20 в комплексное (20.0,0.0) и передает его оператору. Кстати, а что будет с нашей программой, еат ей предложат не к b прибавить 20 (с=Ь+20), а к 20 прибавить b (с-20+b)? Очевидно, ничего хорошего, ведь запись 20.operator+(b) - .это полная бессмыслица, и компилятор наверняка откажется работать, выдав на экран что-то вроде нет подходящего оператора : по match for int + complex & Как же поступить в этом случае? Ведь новый оператор, если им пользоваться, должен во всем напоминать привычный оператор +, равнодушный к порядку слагаемых. Очевидно, операторная функция для него должна принимать два параметра, но как раз это и запрещено функции класса. Единственный выход в данном случае - создать операторную функцию с двумя параметрами вне класса. В этом случае компилятор поймет, что переопределяется оператор сложения, и преобразует <al>+<a2> в operator+(al.a2). Сделает он это только в том случае, когда параметров два. Если их будет, скажем, три, то появится сообщение об ошибке. Правда, операторная функция при определении ее вне класса перестает быть собственной функцией, и ей становятся недоступными тайные области объектов. Значит, придется написать функции setreO и setimO, чтобы извне менять реальную и мнимую части числа. С этими функциями наша операторная функция будет такой: complex operator+Ccomplex al. complex a2){ complex tmp; 1 74 Глава 9. Операторные функции tmp.setre(al.getre()+a2.getre()): tmp.setini(al.getini()+a2.getim()): return tmp; } Правда, выглядит она не очень красиво. Поэтому и языке С++ предусмотрена возможность разрешать некоторым функциям, таким, например, как операторные функции для бинарных операторов, доступ к тайным областям объекта. Такие функции называются дружественными, их прототипы помещаются внутрь класса и предваряются словом friend (друг). Класс complex, использующий дружественную операторную функцию, вместе с сами.м оператором, определенным вне класса, показан в листинге 9.9. Листинг 9.9 Class complex!. public: complex(double* г. double i) : re(r). im(i) {} complex(double r) : re(r). im(0) {} complexO : re(0). im(0) {} friend complex operator+(conp1ex al.complex a2): double getre(){return re;} double getimOJreturn im;} private: double re; double im; complex operator+(complex al. complex a2){ complex tmp; tmp.re=al.re+a2.re; tmp.im-al.im+a2,im; return tnp; Программа, использующая для сложения комплексных чисел оператор, реализованный с помо1цьн) дружественной функции, показана в лисгинге 9.10. друзья и родственники 1 75 Листинг 9.10 #include <iostream> using namespace std; #include complex2.h void out(complex p) {cout p.getreO . p.getimO \n \n; } int main(){ complex a.b(3.2).c(5.4).d; out(a); C=b+20; C=(23.2) out(c): d=a+b+c; d=(26.4) out(d): a=(43.2) a=20+c out(a) return 0; Только что мы использовали дружественную функцию для построения оператора сложения. Но хорошо ли это? Вообще говоря, пе очень. Разрешая доступ извне, объект подвергает себя опасности, потому что пе может полностью кон гролировать свои внутренние переменные. У объекта появляется как бы второй пульт управления, а кто и когда начнет нажимать на нем кнопки - неизвестно. Дружественные функции разрушают представление об объектах, как о чем-то цельном, защищенном от внешних воздействий, и пользоваться ими нужно только в крайнем случае. Однако операторы, реализованные с помощью дружественных операторных функций, действительно очень близки объекту, поэтому в данном случае использование дружественных функций оправданно. Но вряд ли можно оправдать более свободный доступ к объекту с помощью произвольных дружественных функций (не операторных), особенно со стороны другого класса, объявленного как friend:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |