Г. Шилтд - Самоучитель C++ (DJVU) (1114955), страница 27
Текст из файла (страница 27)
В первой части этой главы обсуждается создание оператор-функций — членов класса. О дружественных оператор-функциях будет рассказано далее в этой главе. Здесь представлена основная форма оператор-функции — члена класса: аоааращавиаи~ тлл лма класса:: оретатот$ (список арф умеатоз) // авитолляамал оларацня Часто типом возвращаемого значения оператор-функции является класс, для которого она определена.
(Хотя оператор-функция может возвращать данные любого типа.) В представленной общей форме оператор-функции вме- Глава б. Введение в перегрузку операторов 177 сто знака № нужно подставить перегружаемый оператор. Например, если перегружается оператор +, то у функции должно быть имя орега1ог+. Содержание списка список-аргументов зависит от реализации оператор-функции и от типа перегружаемого оператора. Следует запомнить два важных ограничения на перегрузку операторов.
Вопервых. нельзя менять приоритет операторов. Во-вторых, нельзя менять число операндов оператора. Например, нельзя перегрузить оператор / так, чтобы в нем использовался только один операнд. Большинство операторов С++ можно перегружать. Ниже представлены те несколько операторов, которые перегружать нельзя: й Кроме того, нельзя перегружать операторы препроцессора. (Оператор .* является сугубо специальным и в книге не рассматривается.) Запомните, что в С++ понятие оператора трактуется очень широко: в это понятие входят оператор индексирования Ц, оператор вызова функции О, операторы пев и йе)е1е, операторы .
(точка) и -> (стрелка). Однако в данной главе мы коснемся более обычных операторов. Оператор-функции, за исключением оператора —, наследуются производным классом. Тем не менее для производного класса тоже можно перегрузить любой выбранный оператор (включая операторы, уже перегруженные в базовом классе). Вы уже пользовались двумя перегруженными операторами: « и », которые перегружались для реализации ввода/вывода, Как уже упоминалось, перегрузка этих операторов для реализации ввода/вывода не мешает им выполнять свои традиционные функции левого и правого сдвига. Хотя допустимо иметь оператор-функцию для реализации любого действия— связанного или нет с традиционным употреблением оператора — лучше, если действия перегружаемых операторов остаются в сфере их традиционного использования. При создании перегружаемых операторов, для которых этот принцип не поддерживается, имеется риск существенного снижения читабельности программ.
Например, перегрузка оператора / так, чтобы 300 раз записать в дисковый файл фразу "Мне нравится С++", является явным злоупотреблением перегрузкой операторов. Несмотря на вышесказанное, иногда может потребоваться использовать какой-либо оператор нетрадиционным образом. Типичным примером этого как раз и являются перегруженные для ввода/вывода операторы « и». Однако даже в этом случае, левые и правые стрелки обеспечивают визуально понятный смысл их значения. Поэтому, даже если вам очень хочется перегрузить какой-нибудь оператор нестандартным способом, лучше приложите дополнительные усилия и постарайтесь воспользоваться каким-нибудь более подходящим оператором. И последнее, оператор-функции не могут иметь параметров по умолчанию. Самоучитель С++ 6.2.
Перегрузка бинарных операторов Когда оператор-функция — член класса перегружает бинарный оператор, у функции будет только один параметр. Этот параметр получит тот объект, который расположен справа от оператора. Объект слева генерирует вызов оператор-функции и передается неявно, с помощью указателя й|в. Важно понимать, что для написания оператор-функций имеется множество вариантов. Примеры, показанные здесь и в других местах главы, не являются исчерпывающими, хотя они иллюстрируют несколько наиболее общих технических приемов. име ь 1.
В следующей программе перегружается оператор + относительно класса соогд. Этот класс используется для поддержания координат Х,У. // Перегрузка оператора 4 относительно класса соогд Фтпс1ае)е <товсгеатв> ыв1па пагпеврасе вМ; с1азв соогс1 ( 1пб х, у; // знанения координат рв)з11с: соотс)() ( х = 0; у= 0; ) соопс) (ьпс 1, 1пб з ) ( во1о дее ху(тпй ь1, гпп ьз ) 1 1 = х; 3 = у) соотс) оретасот+ (соотгзо)з2) // Перегрузка оператора + относительно класса соогд соотг соотс):; орегаеот+ (соогс оЬ2) соотг) гетр; ~евр.х = х +оЬ2.х; ~евр.у = у +о)з2.у; ге'птп '-ег1р; гпп па1п !) соотпт о1 (10, 10), о2 (5, 3), оЗ; 1пп х, у; оз = о1 ж о2; // сложение двух обьектов вызов футлсзии орегавот-'; () Глава б. Введение в перегрузкуоператоров сз.пес ху!х, у); соус « "(01 +- о2) Х: " « х « ", Ъ': " « у « "~п"р гегигп О; После выполнения программы на экран выводится следующее: (о1+ о2) Х: 15, У: !3 Давайте внимательно рассмотрим программу.
Функция орега1ог+О возвращает объект типа соогН, в котором сумма координат по оси Х находится в переменной х, а сумма координат по оси У вЂ” в переменной у. Отметьте, что временный объект 1ев)р используется внутри функции орега$ог+О для хранения результата и является возвращаемым объектом.
Отметьте также, что ни один из операндов не меняется. Назначение переменной 1ешр легко понять. В данной ситуации (как и в большинстве ситуаций) оператор + был перегружен способом, аналогичным своему традиционному арифметическому использованию. Поэтому и было важно, чтобы ии один из операндов не менялся. Например, когда вы складываете 10+4, результат равен 14, но ни 10, ни 4 не меняются. Таким образом, временный объект необходим для хранения результата.
Смысл того, что функция орегв$ог+() возвращает объект типа сооп), состоит в том, что это позволяет использовать результат сложения объектов типа соогв в сложном выражении. Например, инструкция оЗ =о1+ о2р правильна только потому, что результат выполнения операции о) + о2 является объектом, который можно присвоить объекту оЗ. Если бы возвращаемым значением бьп объект другого типа, то эта инструкция была бы неправильна. Более того, возвращая объект типа соогв, оператор сложения допускает возможность существования строки, состоящей из нескольких сложений.
Например, следующая инструкция вполне корректна: оЗ = о1 + о2 ~ о! + оЗ; Хотя у вас будут ситуации, в которых понадобится оператор-функция, возвращающая нечто иное, чем обьект класса, для которого она определена, большинство создаваемых вами оператор-функций будут возвращать именно такие объекты.
(Основное исключение из этого правила связано с перегрузкой операторов отношения и логических операторов. Эта ситуация исследуется в разделе 6.3 "Перегрузка операторов отношения и логических операторов" далее в этой главе.) Последнее замечание по этому примеру. Поскольку объект типа соог!! является возвращаемым значением оператор-функции, то следующая инструкция также совершенно правильна: (о! 4 с2) .дес ху(х, у); (во Самоу ттель Здесь временный объект, возвращаемый Функцией орегагог+ (), используется непосредственно.
Естественно, что после выполнения этой инструкции временный объект удаляется. 2. В следующей версии предыдущей програмьи относительно класса ссагпз перегружаются операторы — и =. Перегрузка операторов +, — и = относительно класса соого ()1пс1иое <1ояггеат> пятно пажеярасе ясс; с1аяя соого ( тпс х, у; // значения координат рпЬ11с: соогд() ( х = Ор у= Ор соогс) (1пг 1, 1пс з) ( х =- 1~ у = Э мои дел ху(тп 61, 1пс аз) ( 1 = х; З = У) соогс( орега1ог+ (соогс( оЬ2) соогс) орега1ог- (соогс( оЬ2) 1 соогс( орегажог= (соогг(оЬ2); // Перегрузка оператора + относительно класса соого соогс( соогс(:: орега~ог+ (соого оЬ2) ( соого 1еп~р; гипр,х = х + оЬ2.х; ~евр. у = у + оЬ2.ур гесигп сетпр; Перегрузка оператора — относительно класса соого соогс соого:: орегажог- (соогс(оЬ2) ( соого йель; ~ел~р. х = х — оЬ2 .
х; ~ялмар. у = у — оЬ2.у; гегпгп гещз; // Перегрузка оператора =- относительно класса соога соогс( соогс(::орегасог=(соого оЬ2) ( х = оЬ2.х; у = оЬ2.у; Глава б. Введение в перегружу операторов 181 тебц=п "с)т1з; !! возвращение объекта, которому присвоено значение тпс щаьп() Г соопб о1 (10, 10), о2 (5, 3), оЗ; 1п' х, оЗ = о1 ж о2; !! сложение двух объектов тт вызов функции орекалок () оЗ.цеС ху(х, у) 1 соио « "(о1 ж о2) Х: " « х « ", у: " « у « "~п"1 оЗ = о1 — о2; !! вычитание двух объектов вызов функции орекаеог- () оЗ. деГ ху (х, у) сорб « " (о1- о2) Х: " « х « ", У: " « у « "~п"," оЗ = о1; !! присваивание объекта- вызов функции ореласот= ( ) оЗ.пес ху(х, у]; сопл « "(оЗ = о1) Х: " <с х сс '*, у: " <с у « "',и"т тебцгп 0; Реализация функции орега(ог-О похожа на реализацию функции орегагог+О.
Однако она иллюстрирует ту особенность перегрузки операторов, где важен порядок следования операндов. При создании функции орегаГог+О порядок следования операндов значения не имел. (То есть А+В тождественно В+А.) Однако результат операции вычитания зависит от порядка следования операндов.
Поэтому, чтобы правильно перегрузить оператор вычитания, необходимо вычесть правый операнд из левого. Поскольку левый операнд генерирует вызов функции орега1ог — (), порядок вычитания должен быть следующим: х — оЬ2. х; При перегрузке бинарного оператора левый операнд передается функции неявно, а правый оператор передается функции в качестве аргумента.
Теперь рассмотрим оператор-функцию присваивания. В первую очередь необходимо отметить, что левый операнд (т. е. объект, которому присваивается значение) после выполнения операции меняется. Здесь сохраняется обычный смысл присваивания. Во-вторых, функция возвращает указатель *1й(в. Это 182 Самоучитель Сч-ч. происходит потому, что функция орегагог=() возвращает тот объект, которому присваивается значение.
Таким образом удается выстраивать операторы присваивания в цепочки. Как вы уже должны знать, в С++ следующая инструкция синтаксически правильна (и на практике вполне обычна): а=Ь=с=с(=0; Возвращая указатель "1Ыв, перегруженный оператор присваивания дает возможность подобным образом выстраивать объекты типа соог((.
Например, представленная ниже инструкция вполне корректна: оЗ =.о2 =о1; Запомните, нет правила, требующего, чтобы перегруженная оператор- функция присваивания возвращала объект, полученный в результате присваивания. Однако если вы хотите перегрузить оператор = относительно класса, то, как и в случае присваивания встроенных типов данных, он должен возвращать указатель *ЙЬ. 3. Имеется возможность перегрузить оператор относительно класса так, что правый операнд будет объектом встроенного типа, например, целого, а не объектом того класса, членом которого является оператор-функция. Например, в приведенном ниже примере оператор + перегружается так, что прибавляет целое значение к объекту типа соотг(: // Перегрузка оператора + как для операции оЬ+оЬ, // так и для операции оЬ+хпГ ((1пс1ис(е <1оаггеап> цз(пя патезрасе асс(1 с(авз соотг( ( гпс х, у; 11 значении координат рпЬ11с: соогс(() ( х = Ор у .-- Ор сооЫ (гпс 1, гпс з) ( х = 1т у = 2) чоЫ дес ху(1пГ г11, гпг аО) ( 1 = х; 1 = у; соогс( орегасог+(соогб оЬ2); 11 оЬ + оЬ соогс( орегагог+(гпп 1); /1 оЬ + гпс Перегрузка оператора + относительно класса соогс( соого соогс(::орегагог+(соогс1 оЬ2) соогс) лепр; петр.х = х еоЬ2.х; гетр.у = у +оЬ2,у; гегпгд пепр; Глава б.
Введение в первгруакуолврагоров // Перегрузка оператора + для операции о)э+1пг соотг( соог<(::орегагог+(1лг 1) ( соотг гепзр; гевар.х = х + (; гагар,у = у + (; гейш и Гегвр; 1вг пи1п() соогс! о1 (10„10), о2(5, 3), оЗр глв х, у; оЗ = о1 + о2; // сложение двух объектов // вызов функции орегагог+ (соог<() оЗ. деГ ху (х, у) ) соцс « "(о1 + о2) Х: " с< к « ", у: " с< у « "1л"; оЗ = о1 + 100; // сложение объекта и целого вызов функции орегаког+(1пл) оЗ.дег ху (х, у); соцс с< "(о1 + 100) Х: " <с к сс ", У: " сс у с< "М", гесигп О) Здесь важно запомнить следующее: когда оператор-функция — член класса перегружается так, чтобы объект этого класса можно было использовать в одной операции с переменной встроенного типа данных, встроенный тип данных должен находиться справа от оператора.