Г. Шилтд - Самоучитель C++ (DJVU) (1114955), страница 28
Текст из файла (страница 28)
Смысл этого легко понять: он в том, что объект, который находится слева, генерирует вызов оператор- функции. Однако что произойдет, если компилятор встретит следующую инструкци ю? оЗ = !9 + о1ю // 1п1 + оЬ Для обработки сложения целого с объектом встроенной операции не существует. Перегруженная функция орега1ог+1!п$ !) работает только в том случае, если объект находится слева от оператора. Поэтому эта инструкция приведет к ошибке при компиляции.
(Позже вы узнаете способ обойти это ограничение.) 4. В оператор-функции можно использовать параметр-ссылку. Например, допустимым способом перегрузки оператора + относительно класса свете является следующий: // Перегрузка + относительно класса сооп(, с использованием ссылки соогс( соого::орегагог+(соогс( коЬ2) Самоучитель С++ соотс сеспр; ~юар.х = х + ОЬ2.х; ~е~ар.у = у ь оЬ2.у; тесств сепр; Одним из доводов в пользу использования ссылки в качестве параметра оператор-функции является ее эффективность. Передача объекта функции в качестве параметра часто требует больших затрат процессорного времени.
Передача же адреса объекта всегда быстрее и эффективней. Если оператор многократно используется, параметр-ссылка обычно позволяет значительно повысить производительность. Другой довод в пользу использования параметра-ссылки состоит в том, что ссылка позволяет избежать неприятностей, связанных с удалением копии операнда. Как вы знаете по предыдущим главам, при передаче аргумента по значению создается его копия, Если у такого объекта есть деструктор, то после завершения выполнения функции вызывается деструктор копии. Иногда возможны ситуации, когда деструктор удаляет нечто такое, что необходимо вызывающему объекту.
Использование в этом случае в качестве параметра не самого объекта, а ссылки на этот объект — это простой (и эффективный) способ избежать проблем. Тем не менее, запомните, что в общем случае решить эту проблему могло бы определение конструктора копий. !. Для класса соогй перегрузите операторы * и /. Продемонстрируйте их работу. 2. В приведенном ниже примере некорректно перегружен оператор %, Почему? ооой соог<(:: орега~огЪ (соотт1 оЬ) ( с(оиЫс сосС « "Вветвтте число: с)в >> З; соцв « "корен. " « т « " равен "„. сонг « а~~(!.); 3 Поэкспериментируйте, меняя тип возвращаемого значения оператор-функций на что-нибудь отличное от соогй. Обратите внимание на генерируемые компилятором сообщения об ошибках.
Глава 6. Введение в перегрузку операторов 6.3. Перегрузка операторов отношения и логических операторов Существует возможность перегрузки операторов отношения и логических операторов. При перегрузке операторов отношения и логических операторов так, чтобы они вели себя обычным образом, не нужны оператор-функции, возвращающие объект класса, для которого эти оператор-функции определены. Вместо этого они должны возвращать целое, интерпретируемое как значение 1гве или Ызе.
Помимо того, что возвращаемым значением таких оператор-функций должно быть значение 1гце или Га1зе, должна быть возможность встраивания операторов отношения и логических операторов в большие выражения, включающие также данные других типов. и - ~ф~$4ФФВМ~~ ,.'"ч 4; В современных компиляторах С++ оператор-функции для перегрузки операторов отношения и логических операторов могут возвращать значения булева типа, хотя особого преимущества в этом нет. Как было описано в главе Г, в булевом типе данных определены только два значения — Ггие и Фа!ве.
Эти значения автоматически конвертируются в ненулевое и нулевое значения. И наоборот, целые ненулевое и нулевое значения автоматически конвертируются в значения тгие и Фа!ве. ~~ф имеры~~! 1. в следующей програюие перегружаются операторь == и 8итг: (/ Перегрузка операторов == и йй относительно класса соогс) ()ьпс1дс)е <ьояггеап~> пятна пащоярасе я(х(; с1аяя соотг 1пс х, у; !/ значении координат риЬ11с. соогс)() ( х = Ор у= Ор соотг(тпрр 1, 1пс 3) ( х = 1~ у = 3~ чоЫ дев ху(ьпс Вь, ьп аЗ) ( 1 = х; 1 = у; ) тпс орегагог== (соого оЬ2); гпс орегаьога а(соотг оЬ2] р /2 Перегрузка оператора == для класса соотг) тпрр сооЫ::орега1ог==(соогс( оЬ2) гегпгп х==оЬ2.х аь у==оЬ2.у; Г Самоучитель С-ьь Перегрузка оператора йй для класса соогс) 1пг соогс)::орегаЬогйй(соогс( оЬ2) ( гееигп (х йй оЬ2.х) йй (у йй оЬ2, у); 1 тпг тпайп() соогс) о1(1О, 1О), о2(5, 3), оз(10у 10) р о4 (О, О) ) соне « "о1 равно о2~п"; « "о1 не равно 02'~п"; 1Г.
(о1==о2 ) е1ве сенс о1 равно оз~п"Г равно оЗ1п'"; гг(о1==оз) е1ве сонь соус « « "о1 не сонг « "о1 йй о2 равно истина~п"; « "о1 йй о2 равно ложь~п"; 1Г(о1ййо2) е1ве сонг 1Г(о1ййо4) соне « "о1 йй о4 равно истина1п"; е1ве соне « "о1 йй о4 равно ложь1п"; геснгп О; 1. Относительно класса соог(1 перегрузите операторы отношения < и >. 6.4. Перегрузка унарных операторов .. Приме~ 1. В следующей программе относительно класса соотг перегружается оператор инкремента (++): // Перегрузка оператора +-ь относительно класса соотс( $1пс1пс)е <1овсгеасп> пв1па пагпеврасе вес(; Перегрузка унарных операторов аналогична перегрузке бинарных, за исключением того, что мы имеем дело не с двумя, а с одним операндом.
При перегрузке унарного оператора с использованием функции-члена у функции нет параметров. Поскольку имеется только один операнд, он и генерирует вызов оператор-функции. Другие параметры не нужны. Глава б Введение олерагпоров в перегрузку с!авв соогс1 1пГ. х, у; // значения координат рцЫ1с: соогд () ( х = С; у = С; соопй(1пе з, упЕ Л) ( х = 1; у .- Л' гогс) деГ ху(1пк а1, упь ЯС) ( 1 = х; > =- у; ) соогс) орека1ок++ () ) Перегрузка оператора ++ для класса соогб соо а соого.":орегасок++() х++; у+в ~ гегпгп *гИвг )п( та гп ( ) соогс1 о1(10, 10) (пг х, у; ++о1г // инкремент объекта о!.асг ху(х, у); соц( с< "(++о1) Х: " « х « ", У; " « у « "~п"; гегцгп Оз Поскольку оператор инкремента увеличивает свой операнд на единицу, перегрузка этого оператора меняет объект, с которым он работает. Это позволяет использовать оператор инкремента в качестве части более сложной инструкции, например, такой: о2 = ++о1; Как и в случае бинарных операторов, нет правила, которое заставляло бы перегружать унарный оператор с сохранением его обычного смысла.
Однако в большинстве случаев лучше поступать именно так. 2. В ранних версиях С++ при перегрузке оператора инкремента или декремента положения операторов ++ и — относительно операнда не различались. Поэтому по отношению к предыдущей программе следующие две инструкции эквивалентны: о1++г ++о1„. Однако в современной спецификации С++ определен способ, по которому компилятор может различить эти две инструкции. В соответствии с этим /88 Самоучитель способом задаются две версии функции орега1ог++О. Первая определяется так, как было показано в предыдущем примере. Вторая определяется следующим образом: соотг( соске):: орекакок++ (Ьпк посазеЮ; Если оператор ++ указан перед операндом, вызывается функция орега(ог++().
Если оператор ++ указан после операнда, вызывается функция прага(ог++(1п1 по1пае(1). В этом случае переменной поФнае(1 передается значение О. Таким образом, если префиксный и постфиксный инкремент или дскремент важны для объектов вашего класса, то понадобится реализовать обе оператор - функции. 3. Как вы знаете, знак минус в С++ является как бинарным, так и унарным оператором.
Вы, наверное, хотели бы знать, как его можно перегрузить относительно создаваемого вами класса так, чтобы оператор сохранил оба эти качества. Реальное решение достаточно элементарно: просто перегрузите его дважды, один раз как бинарный оператор, а второй — как унарный. Программа, реализующая этот прием, показана ниже: // Перегрузка оператора — относительно класса соотг( ()тпс1нс(е <тоаккеагт> нз(па папзезрасс зМ; с)азз соотг( ( 1пс х, у; // значения координат рпЬ11с: соокб() ( х = О; у= О; соске)(тпк Ь, тпк з) ( х = т; у = 5; ~ оЫ рек ху(тпк ат, ьпс 63 ) ( т = х; 3 = у; соокс) орекасок- (соске(оЬ2); // бинарный минус соогс( Орекакок- (); // унарный минус // Перегрузка оператора — относительно класса соске( соого соотг):: орекакок- (соокс(оЬ2) соокб Кеир; кедр.х = х — оЬ2.х; кегар.у = у — оЬ2.у; гекпкп Кепр; // Перегрузка унарного оператора — для класса сооп1 соотг) соске(::орекакок-() Самоучитель С++ оператор-функцию, за одним важным исключением, которое будет рассмотрено в примерах.
Нельзя использовать дружественную функцию для перегрузки оператора присваивания. Оператор присваивания можно перегружать только как оператор-функцию — член класса. 1. Здесь функция орега1ог+О перегружается для класса соог(1 с использованием дружественной функции: О Перегрузка оператора + относительно класса соотг // с использованием дружественной функции ()1пс1пс(е <ьозсгеатв> из(пя паптезрасе з1с(; с1азз соотг ( (вг х, у; 22 значения координат рпЬ11с; с)() (х=0;у=с; соотг(ьпг 1, 1пг )) ( х= 1; у= 3; ) чоЫ дег ху(1ПЬ ьь, тпг аЗ) ( 1 = х; З = У; ггьепс) соотг орегасог+(соогг( оЬ1, соотг оЬ2); Перегрузка оператора ж с использованием дружественной функции соотг орегавог+(соогс( оЬ1, соогс оЬ2) ( соогц Гетпр; ~етр,х = оЬ1.х ж оЬ2.х; 'оетр.у = оЬ1.у ж оЬ2.у; гегигп сетпр; ьпг паьп () соогс( о1 (10, 10), о2 (5, 3), озп тпг х, у; оЗ =- о1 + о2; 22 сложение двух объектов вызов функции орегагог+() 191 Глава 6.
Введение в перегрузку операторов оэ.дел ху(х, у); сочв« "(о1ч-о2) Х: "«х«", т'. "«у« гегпгп О; Обратите внимание, что левый операнд передается первому параметру, а правый — второму. 2. Перегрузка оператора посредством дружественной функции дает одну очень важную возможность, которой нет у функции — члена класса. Используя дружественную оператор-функцию, в операциях с объектами можно использовать встроенные типы данных, и при этом встроенный тип может располагаться слева от оператора. Как отмечалось ранее в этой главе, можно перегрузить оператор-функцию, являющуюся членом класса, так, что левый операнд становится объектом, а правый — значением встроенного типа.