|
Программирование >> Аргументация конструирования
Нет правила, заставляющего функцию operator+(USDollarS, USDollarS;) осуществлять именно сложение. Вы можете заставить функцию operator+ () выполнять любые действия; однако выполнение этим оператором чего-либо, кроме сложения, очень-очень плохая идея. Люди привыкли к тому, что их операторы выполняют определенные действия. Вряд ли им понравится, если привычные операторы начнут выполнять непривычные действия. Оператор += не имеет понятия, как нужно скомбинировать операторы + и =. Таким образом, каждый оператор должен быть перегружен отдельно. Если вы определили только один оператор- operator+ + () или operator-(), он будет иснользован как для нрефиксной, так и для постфиксной формы. Правда, стандарт C + + не требует от компилятора такой сообразительности, однако большинство компиляторов C++ умеют это делать. Изначально в C + + не было возможности переопределять префиксный оператор ++х отдельно от его постфиксной версии х++. Однако многим программистам это не нравилось, поэтому правило изменили. В соответствии С ЭШМ правилом operator + + (className) ОШЭСИГО! К префиксному оператору, a operator + + (ClassName, int) - к постфиксному. В качестве второго аргумента при этом всегда передается 0. То же правило распространяется и на оператор декремента -- . В работе такие операторы оказываются довольно удобными. Что может быть проще, чем строка d3 dl + d2 или ++d3? щ4 HaqftoHee? Почему аtort () возвращает сумму по значению, a operator + + () возвращает увеличенный на единицу объект по ссылке? Это не случайность, здесь кроется очень большое отличие между этими операторами! Мы начинаем осваивать весьма сложную для понимания часть перегрузки операторов, в которой легко запутаться и которую трудно отлаживать. operator+() Сложение двух объектов не приводит к изменению ни одного из этих объектов. Таким образом, а + Ь не изменяет ни а, ни Ь, а значит, operator+ () не должен сохранять результат сложения в какой-то из этих переменных. Очень неудачная мгсль выполнять сложение так, как это сделано в данной программе, поскольку будет изменяться значение одного из аргументов USDollari operator+(USDollarS si, USDollarS s2) si.cents += s2 -cents; if (si.cents >=100) si.cents -= 100 ; s1.dollars++; si.dollars += s2.dollars; return sl; Проблема в том, что в результате такого простого присвоения, ка ul = u2 т u3;, будут изменены значения и ul и и2. Чтобы избежать этого, operator+() должен создавать временный объект, в котором и будет сохранен результат сложения. Поэтому operator+ () конструирует собственный объект, который возвращается этой функцией. Однако при этом нельзя забывать и еще кое-что! Например, приведенный ниже фрагмент работать не будет. USDollars opeiator+(USDollars si, USDollarS s2) unsigned int cents - si.cents + s2.cents; unsigned int dollars = si.dollars т s2.dollars; USDollar result(dollars, cents); return result Распространенная ошибка № 1. Хотя этот фрагмент откомпилируется без со общений об ошибке, результат выполнения этой функции будет весьма плачевным. Проблема в том, что возвращается ссылка на объект result, который является локальным для данной функции. Таким образом, к тому времени, как вызывающая функция сможет использовать возвращаемый результат, объект result уже выйдет из области видимости. Тогда почему бы нам не выделить блок памяти из кучи так, как это сделано в приведенном ниже примере? USDollars operatort (USDollars si, USDollarS s2) f unsigned int cents = si.cents + s2.cents; unsigned int dollars = si. dollars т s2. dollars; return *new USDollar(dollars, cents); Распространенная ошибка № 2, Вы, конечно, можете вернуть ссылку на объект, память под который была выделена из кучи, однако возникнет новая проблема: при этом не предусматривается механизм возврата памяти в кучу. Эта ошибка называется утечкой памяти, и ее очень сложно отыскать. Хотя такой оператор и будет работать, он будет потихоньку истощать память в куче при каждом выполнении сложения. Возврат по значению заставляет компилятор создавать временный объект в стеке вызывающей функции. Затем созданный функцией объект копируется в этот временный объект. Возникает вопрос: как долго существует временный объект, который возвращает operator+ () ? Изначально это не было определено, однако затем создатели стандартов собрались вместе и решили, что такой временный объект остается необходимым до завершения развернутого выражения. Развернутое выражение - это все, что находится перед точкой с запятой. Рассмотрим, например, такой фрагмент: SomeClass f ( ) ; LotsClass g 0 ; fn().. mt i; i = f!) U-g{)); Временный объект, возвращенный функцией f (), существует, пока выполняется функция д() и пока выполняется умножение. Тамде стоит точка с запятой, этот объект уже недоступен. operator+ + () В отличие ator+ (), функции loe++ (} модифицирует свой аргумент. А значит, вам не нужно создавать временный объект иди возвращать результат но значению. Вычисляемый результат можно хранить прямо в s. Вызывающей функции может быть возвращен предоставленный оператору аргумент. это будет отлично работать USDollarS operator++(USDollarb s) s.cen ts + +; nts 100) £.cents -=10 0; £.dollars++; return s; приведенный ниже пример, который содержит одну очень хитрую ошибку. это не очен ;{сная версия USDollar operatorT+(uSDollaru s) s.cents** ; if (£;. cents >= 100) s . cents - = 100; s.dollars++; return s; Распросграненная ошибка № 3. Возвращая значению, функция застав- ляет компилятор генерировать копию объекта. Это отлично сработает в выражениях тина а = 4+Ь, но что будет с выражениями тина ++(++а)? Мы ожидаем, что а будет увеличено на 2. Однако при приведенном выше переопределении этого оператора, объект а будет увеличен на 1, а затем на 1 будет увеличена его копия, а не сам объект а. Конструкция вида +* (*+а) не очень распространена, но все же допустима. В любом случае имеется еще множество примеров, в которых такой оператор не будет работать правильно. Можно сформулировать следующее правило: если оператор изменяет значение своего аргумента, возвращайте аргумент по ссылке. Если оператор не изменяет значения своих аргументов, создавайте новый объект и возвращайте его по значению. Входные аргументы лучше всегда передавать по ссылке.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |