|
Программирование >> Унарные и бинарные операторы
1 76 Глава 9. Операторные функции Class А{ friend class В: При таком объявлении В-этодружествишый А класс, то есть собственные функции В имеют доступ к внутренним структурам класса А, что не только опасно, но и неудобно. Ведь изменение внутренних структур класса А с большой вероятностью скажется на его дружественном классе В, имеющем доступ к этим структурам. Иными словами, нарушается важнейший принцип падежного программирования: последствия изменений в программе должны быть как можно локальней: перемены внутренних структур класса должны (в идеале) сказываться только на устройстве его собственных функций. Но чем больше дружественных классов и функций в программе, тем больше она становится похожей на кошмарное минное поле, где разминирование в одном месте приводит к появлению нескольких новых мин, причем в самых неожиданных местах. Ввод-вывод До сих пор нам казалось, что объект cout и оператор волшебным образом знают, как выводить на экран объекты разных типов. Но дело, конечно не в волшебстве, а в том, что все эти объекты (строки, переменные int и double) стандартны, стоит попытаться вывести на экран объект класса conplex, о котором оператору ничего не известно, и компилятор сообщиг. operator not implemented in type ostream for arguments of type complex in function mainO Заметим, что дружба в С++ ие взаимна. Если класс В объявлен дружественным для А классом, то А ие становится от этого другом В. Для этого необходимо специальное объявление в классе В: friend class А. Ввод-вывод 177 В этом сообщении, переводимом как оператор , принадлежащий классу ostream, не создан для аргументов типа cofrpl ех , есть важная информация. До сих пор мы не знали, к какому классу относится объект cout. Компилятор подсказывает нам: это класс ostream, и теперь у нас есть все необходимое, чтобы построить операторную функцию для вывода комплексных чисел. Затруднение только в том, что оператор нельзя определить в созданном нами классе complex. Ведь для этого вьшод неременной нужно записать следуюгцим образом: complex а: а cout: Только при такой записи компилятор вызовет операторную функцию а.operator (), которая сможет вывести на экран комплексное число. Но в С++ принята другая запись cout а, что приводит к вызову одной из операторных функций (а именно cout.operator ()) класса ostream. В этом классе не предусмотрен вывод переменных нашего класса compl ex, что и вызьшает сообщение об ошибке. Значит, вывод на экран обычным для С++ способом (cout а;) требует либо изменения стандартного класса ostream, что нежелательно, либо использования внешней функции с двумя параметрами: первым должна быть ссылка на объект cout, вторым - объект класса complex. Если объявить операторную функцию дружественной классу conplex, то выглядеть она будет так, как показано в листинге 9.11. Листинг 9.11 ostream& operator (ostream& stream, conplex a){ stream a. re . a. i m \n \n: return stream: } Увеличение Эта глава неожиданно оказалась длинной, но картина не будет полной, если пройти мимо унарных операторов ++ и --. Оператор ++ (для оператора - все аналогично) можетбыть определен в соответствуюп1емклассе, в этом случае операторной функщн! operator++() пе нужны параметры, достаточно ука.зателя па текущий объект this. Попробуем переопределить оператор ++ для одного из известных нам классов. Объект этого класса должен быть таким, чтобы автоувеличение смотрелось так же естественно, как и для родных переменных типа int. Вряд лн стоит определять оператор++ для объектов класса complex, потому что для них увеличение пе имеет явного смысла (что увеличивать - реальную часть, мнимую часть или, быть может, обе?). Поэтому вспомним о классе mycl оск, созданном в раз-Деле Богатые и бедные классы главы 8. Для объектов этого класса автоувеличепие вполне естественно: это перевод часов на секунду вперед. Соответствуюи1ая операторная функция показана в листинге 9.12. Листинг 9.12 myclock & operator++(){ tickO; return *this: Эта операторная функция сначала переводит стрелки на секунду вперед функцией tick(), а затем возвращает ссылку на измененный объект. Очевидно, эта функция реализует префиксный оператор, ведь объект возвращается после перевода стрелок. Програ.мма, использующая такой оператор, показана в листинге 9.13. Встретив в исходном тексте программы инструкции сопрТех а и cout а, компилятор вызовет операторную функцию из листинга 9.11 и передаст ей два аргумента: ссылку на объект cout (объект класса ostream) и объект а класса complex. Внутри операторной функции реализуются действия, характерные для оператора класса ostream: вывод чисел типа double и последовательностс11 символов. Возвращает напш операторная функция ссылку на объект класса ostream - с тем, чтобы обеспечить вывод объектов по цепочке. Представим, что на экран выводятся несколько объектов: cout А в С: Если учесть, что оператор независимо от переопределений всегда выполняется слева направо, то сначала выполнится часть cout А. Если обтект А известен классу ostream, вызывается операторная функция cout.operator (A), которая возвращает ссылку, то есть другое имя объекта cout. Так что можно сказать, что после вывода А цепочка укоротится: cout в <<: Если объект В не известен классу ostream (потому, например, что это объект созданного нами типа complex), то вызывается внешняя операторная функция, такая как в листинге 9.11. Но поскольку она принимает и возвращает ссылку на ostream, порядок вывода не наруннп-ся: в зависимости от пта объектов будут вызьшатыя либо операторные функции класса ostream, либо внешние операторные функции, подобные той, что показана в листинге 9.11. И так будет до тех пор, пока не исчерпается список выводи.мых объектов. Задача 9.3. Переопределите оператор для класса myclock, описанного в разделе Богатые и бедные классы главы 8. рый должен увеличивать время после использования объекта. Если говорить об объектах класса myclock, то после выполнения следующих инструкций часы b должны показывать 10:59:30, а часы а - 10:59:31: clock a(10.59.30).b: b=a++; Но как компилятор поймет, что оператор постфиксный? Очевидно, нужен какой-то знак, отличаюпшй его от префиксного, и создатели С++ решили использовать для этого фиктивный целочисленный параметр (int) в определении операторной функции. Функция с таким параметром, показанная в листинге 9.14, реализует постфиксный оператор ++. Листинг 9.14 myclock operator++(int){ myclock tmp=*this: tickO; return tmp: Здесь сначала создается временная переменная tmp, где запоминается TCKyniee состояние объекта (this - указатель на текущий объект, оператор * превращает указатель в сам объект, а присваивание tnip=*this приводит к тому, что объект tmp запоминает состояние текущего обьекта). Далее стрелки объеюга *this переводятся на секунду вперед, и временный объект возвращается во внешний мир. Обратите внимание, возвран1ается объект, а не ссылка на него. Это значит, что постфиксному оператору нельзя присвоить значение, он должен стоять справа от оператора присваивания (листинг 9.15). Листинг 9.15 #include <iostream.h> using namespace std; #include myclock4.h int main(){ Листинг 9.13 #inclucle <iostreani.h> using namespace std; #include myclockS.h int main(){ myclock a(10.59.Z9); for(int i=0; i<1000:i++) cout ++a; return 0; } В файле myclockS.h вместе с другими собственными функциями класса myclock есть и операторная функция для префиксного оператора ++. Кроме того, там определен и оператор . Если вам не удалось решить задачу 9.3, можете найти ответ в этом файле. Программа из листинга 9.13 показывает 1000 значений времени, начиная с 10 часов 59 минут и 30 секунд, псггому что значение, установленное конструкторо.м, перед выводом на экран (10:59:29) увеличивается на секунду оператором ++. Обратите внимание на то, что префиксный оператор, операторная функция для которого показана в листинге 9.12, возвращает ссылку на объект а. Как мы знаем, функции (а оператор - это специальная функция), возвращающей ссылку, можно присвоить значение, поэтому объекту а, если оператор ++ определен так, как в листинге 9.12, можно в полном соответствии со сгаи-дартом языка С++ присвоить значение ++а-Ь. Смысла в этом немного, ведь, несмотря на увеличение, объект а все равно станет равным Ь, но, по крайней мере, стандарт это разрешает. Можно также использовать цепочку операторов ++++а, в которой один оператор ++ передает ссылку на объект другому оператору ++, и в резул ьтате время увеличивается на две секунды. Построив операторную функцию для префиксного оператора, перейдем к постфиксному оператору, кото-
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |