Информационный сайт

 

Реклама
bulletinsite.net -> Книги на сайте -> Программисту -> Мизрохи С.В. -> "Turbo Pascal и объектно-ориентированное программирование" -> 31

Turbo Pascal и объектно-ориентированное программирование - Мизрохи С.В.

Мизрохи С.В. Turbo Pascal и объектно-ориентированное программирование — М.: Финансы и статистика , 1992. — 192 c.
ISBN 5-279-00903-2
Скачать (прямая ссылка): efektispolzc2000.djvu
Предыдущая << 1 .. 25 26 27 28 29 30 < 31 > 32 33 34 35 36 37 .. 105 >> Следующая

Имеет смысл записать это выражение в эквивалентной функциональной форме. Если только вы не «кабинетный» программист на языке LISP, нижеследующий пример заставит вас оценить операторы:
w.operator=(х.operator=(у.operator=(z.operator=("Hello") ) )) ;
Данная форма приведена для иллюстрации, поскольку она показывает, что аргументом для w. operator=, х. operator= и у. operator= является значение, возвращаемое предыдущим вызовом функции operator=. В результате тип, возвращаемый функцией operator=, должен быть приемлемым в качестве аргумента этой же функции. В случае версии по умолчанию operator= для класса С общий вид функции выглядит следующим образом (см. правило 45):
С& С:: operator=(const С&); ''
Как правило, соглашение о том, что operator= принимает и возвращает ссылку на объект класса, выполняется, хотя иногда можно перегрузить operator= так, чтобы он принимал аргументы различных типов. Например, стандартный тип string поддерживает две различных версии оператора присваивания:
strings
operator=(const strings rhs); Il Присвоить string объекту типа string-strings
operator=(const char.*rhs) ; // Присвоить char * объекту типа string.
Правило 15 III

Заметьте, однако, что даже при перегрузке возвращаемый тип является ссыл-кой на объект класса.
Типичная среди начинающих программистов С++ ошибка - возвращать для operator= тип void. Данное решение будет казаться разумным до тех пор, пока ВЫ не осознаете, что оно препятствует последовательным присваиваниям. Вот почему этого делать не следует.
Другая распространенная ошибка - возвращать ссылку на объект const, как показано ниже:
class Widget { public:
... Il Заметьте: возвращается значение с const.
const Widgets operator=(const Widgets rhs); 1
};
Обычно аргументация сводится к тому, что надо уберечь пользователя от искушения наделать глупостей, скажем, такого рода: ¦ 4
Widget wl, w2, w3; * * *'
1 = w2) - w3; // Присвоить wl значение w2 , а затем результату Il присвоить w3!(Если бы operator= класса Widget Il возвращал константную ссылку, этот код не прошел // бы компиляцию.) , ' < \ .,^i \
Как ни глупо это выглядит, но такое присваивание не запрещено для встроенных типов:
int il, І2, ІЗ;
(il = І2) = ІЗ; // Допустимої Присвоить il значение І2, а затем ^ // присвоить il значение ІЗ.
Я не слышал о практическом использовании вещей подобного рода, но если это приемлемо для int, то подходит и для моих классов. Это должно пригодиться и вам. Зачем создавать себе лишние проблемы из-за несовместимости с соглашениями, принятыми для встроенных типов?
Для оператора присваивания, следующего общему соглашению, есть два кандидата на роль возвращаемого объекта: объект с левой стороны знака присваивания, указываемый при помощи this, и объект с правой стороны, содержащийся в списке аргументов. Какой из них будет правильным?
Ниже перечислены возможности, которыми мы располагаем при работе с классом String (класс, для которого, как свидетельствует правило 11, в обязательном Порядке требуется оператор присваивания):
Strings String::operator=(const Strings rhs) {
return *this; 11 Возвращаем ссылку на объект слева.
Конструкторы и операторы
1¦¦1Ii
}
Strings string::operator=(const Strings rhs) {
return rhs; Il Возвращаем ссылку на объект справа.
}
Может показаться, что указанные возможности практически идентичны, однако между ними имеется существенное различие.
Во-первых, версия, возвращающая rsh, не будет компилироваться; данное обстоятельство объясняется тем, что rsh - это ссылка на const String, а оператор = возвращает ссылку на String. Пока для объекта, объявленного с const, вы будете пытаться вернуть неконстантную ссылку, компилятор не даст вам покоя. Может показаться, что этого достаточно легко избежать, - просто нужно переопределить operator= следующим образом:
Strings String::operator=(Strings rhs) {... }
Увы, теперь не будет компилироваться код пользователя! Давайте опять взглянем на последнюю часть цепочки равенств:
X= "Hello"; //Тоже, что и х. operator= ("Hello")
Поскольку аргумент справа от знака равенства имеет неправильный тип - это массив char, а не String, - для того чтобы вызов функции прошел успешно, компилятору придется создавать временный объект String (посредством конструктора String). Иными словами, будет сгенерирован код, приблизительно эквивалентный следующему: ,,
const String temp("Hello") ; II Создание временного объекта. X = temp; Il Передача его оператору =.
Компилятор пытается создать такие временные объекты (если необходимый конструктор не объявлен как explicit, см. правило 19), однако обратите внимание на то, что временный объект объявлен с const. Это важно, поскольку предотвращает случайную передачу временных объектов в функции, которые модифицируют свои аргументы. Если бы такая передача была возможна, программисты с удивлением обнаружили бы, что модифицируется только сгенерированный компилятором временный объект, а не сам аргумент, переданный ими в функцию. (Мы знаем это точно, поскольку ранние версии С++ разрешали подобного рода генерацию, объекты передавались функции и модифицировались, а в результате только возрастало число недоумевающих программистов.)
Предыдущая << 1 .. 25 26 27 28 29 30 < 31 > 32 33 34 35 36 37 .. 105 >> Следующая
Реклама
Авторские права © 2009 AdsNet. Все права защищены.
Rambler's Top100