Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 55
Текст из файла (страница 55)
Создадим оператор преобразования специально для класса Т)тгееО, чтобы продемонстрировать его применение. Допустим, что требуется преобразовать объект типа тпгееО в целое значение, чтобы затем использовать его в целочисленном выражении. Такое преобразование требуется, в частности, для получения произведения всех трех координат объекта. С этой целью мы воспользуемся следующей неявной формой оператора преобразования: риЫ1с всас1с 1ыр11сгг орегагог гпк(ТЬгеео ор1) ( гекигп ор1.х * ор1.у * ор1.г; ) Ниже приведен пример программы, демонстрирующей применение этого оператора преобразования. // Пример применения оператора неявного преобразования.
иа1по зузсевц // Класс дпя хранения трехмерных координат. с1аав ТпгееР ( 1пг х, у, г; // трехмерные коорлинаты риЬ11с Тпгеео() ( х = у = г = О) риЫас Тьгееп(1пк г, 1пг 1, 1пг К) ( х = 1) у = 1; г = Кт // Перегрузить бинарный оператор +. риы1с агагас тпгеео орегагог +(тпгееп ор1, тнгееп ор2) ( Тпгееп геви1Г = пен Тпгеео()т гези1с.х = ор1.х + ор2.хт геви1г.у = ор1.у т ор2.ут геаи1с.г = ор1.г + ор2.г) 280 часть (. Нзык Са геснгп геян1Г; ) // Неявное преобразование объекта типа ТЬгеео // к типу 1пс. ривттс ясас1с ыпр11с1Г орегасог гпс(ТЬгеео ор1) ( гегзгп ор1,х * ор1.у * ор1.я) // Вывести координаты Х, у, Е.
риоттс чо1б ВЬЮ() ( Сопяо1е.нг1сеогпе(х + ", " ь у + ", " + я); ) ) с1аяя тпгееооево ( ясастс чо1б Матп() Тпгеео а = пею ТЬгеео(1, 2, 3); тпгееО Ь = пен тпгееО(10, 10, 10) тпгеео с = пен ТЬгеео(); гпс 1: Сопяо1е.иг1ге("Координаты точки а: ")) а.ЯЬон(); Сопяо1е.игтсеьтпе(); Сопяо1е.нг1се("Координаты точки Ь: "]; Ь.ЗЬон(); Сопяо1е.Игтсеоапе()г с = а + Ь; // сложить координаты точек а и Ь Сопяо1е.Игтсе("Результат сложения а + Ь: "); с.зпон() ) Сопяо1е.Иг1сеотпе В; 1 = а) // преобразовать в тип тпс Сопяо1е.нгтсеотпе("Результат присваивания 1 = а: " Ь 1); Сопяо1е.Игтсеотпе()) 1 = а * 2 — Ь; // преобразовать в тип 1пс Сопзо1е.нгтсевтпе("Результат вычисления выражения а * 2 — Ь: " + 1) ) Вот к какому результату приводит выполнение этой программы: Координаты точки а: 1, 2, 3 Координаты точки Ь: 10, 10, 10 Результат сложения а + Ь: 11, 12, 13 Глава 8.
Перегрузка операторов 281 Результат присваивания г = а: б Результат вычисления выражения а * 2 - Ь: -988 Как следует из приведенного выше примера программы, когда объект типа ТЬгееО используется в таком целочисленном выражении, как 1 = а, происходит его преобразование. В этом конкретном случае преобразование приводит к возврату целого значения 8, которое является произведением координат точки а, хранящихся в объекте того же названия. Но если для вычисления выражения преобразование в тип 1пС ие требуется, то оператор преобразования ие вызывается. Именно поэтому операторный метод орегасог 1пС() не вызывается при вычислении выражения с = а + Ь. Однако для различных целей можно создать разные операторы преобразования.
Так, для преобразования объекта типа ТЬгееО в тип с)оиЬ1е можно было бы определить второй оператор преобразования. При этом каждый вид преобразования выполнялся бы автоматически и независимо от другого. Оператор неявного преобразования применяется автоматически в следующих случаях: когда в выражении требуется преобразование типов; методу передается объект; осуществляется присваиваиие и производится явное приведение к целевому типу. С другой стороны, можно создать оператор явного преобразования, вызываемый только тогда, когда производится явное приведение типов.
В таком случае оператор явного преобразования ие вызывается автоматически. В качестве примера ниже приведен вариант предыдущей программы, переделанный для демонстрации явного преобразования в тип гпс. // Применить явное преобразование. ив1пс Бузгеик О Класс для хранения трехмерных координат. с1азз Тпгеев ( 1пг х, у, г; // трехмерные координаты риътго ТЬгееО() ( х = у = г = 0; ) риь11с тьгееб(гпс 1, гпс 1, 1пс х) ( х = 1; у = 1; з = кк ) // Перегрузить бинарный оператор +. рив11с згаС1с ТЬгееб орегагог +(Тпгееб ор1, ТЬгееб ор2) ( ТЬгеео гееи1С = пеи ТЬгеео(); гези1С.х = ор1.х + ор2.х; гези1С.у = ор1.у т ор2.у; гези1г.з = ор1.г ь ор2.г; геСигп гези1С) ) // Выполнить на этот раз явное преобразование типов.
ривтсо згагьо ехр1гс1С орегаСог гпг(ТЬгееп ор1) ( гегигп ор1.х * ор1.у * ор1.з; // Вывести координаты Х, Т, Е. риътго чогб Япои() 282 Часть ). Язык С№ ( Сопэо1е.иг1се01пе(к + ", " + у + ", " + я) т ) с1ава Тпгееооеио ( всаскс чо№б Ма1п() ( Тпгеео а = пен Тпгееп(1, 2, 3)т Тпгеео Ь = пен Тпгееп(10, 10, 10)т Тпгеео с = пен Тпгеео()т Тпс ).т Сопзо1е.Игьсе("Координаты точки а: ")т а.эвон()) Сопэо1е.игьгеъьпе() т Сопэо1е.итаке("Координаты точки Ь: ")т Ь.зпон () ' Сопэо1е.игьсевьпе()т с = а + Ьт // сложить координаты точек а и Ь Сопво1е.иг1се("Результат сложения а + Ь: "); с. 5пон (] т Сопво1е.игасеъапе()т Сьпс) ат // преобразовать в тип 1пс явно, // поскольку указано приведение типов Сопяо1е.иг№ГЕОвпе( "Результат присваивания 1 = а: " + 1)т Сопво1е.иг1сеьапе()) (Тпс)а * 2 — (1пс)Ь) // явно требуется приведение типов Сопзо1е.игьсеъьпе( "Результат вычисления выражения а * 2 — Ь: " + 1)т ) ) Оператор преобразования теперь указан в явной форме, и поэтому преобразование должно быть явно приведено к типу 1пе.
Например, следующая строка кода не будет скомпилироваиа, если исключить приведение типов: 1 = (Тпс) ат // преобразовать в тип Тпс явно, // поскольку указано приведение типов На операторы преобразования накладывается ряд следующих ограничений. ° Исходный или целевой тип преобразования должен относиться к классу, для которого объявлено данное преобразование. В частности, нельзя переопределить преобразование в тип 1пт, если оио первоначально указано как преобразование в тип бопЬ1е. ° Нельзя указывать преобразование в класс оЬ2 есС или же из этого класса. ° Для одних и тех же исходных и целевых типов данных нельзя указывать одновременно явное и неявное преобразование, Глава 9. Перегрузка операторов 283 ° Нельзя указывать преобразование базового класса в производный класс. (Подробнее о базовых и производных классах речь пойдет в главе 11.) ° Нельзя указывать преобразование в интерфейс или же из него.
(Подробнее об интерфейсах — в главе 12.) Помимо указанных выше ограничений, имеется ряд рекомендаций, которыми обычно руководствуются при выборе операторов явного или неявного преобразования. Несмотря на все удобства неявных преобразований, к ним следует прибегать только в тех случаях, когда преобразованию не свойственны ошибки. Во избежание подобных ошибок неявные преобразования должны быть организованы только в том случае, если удовлетворяются следующие условия.
Во-первых, информация не теряется, например, в результате усечения, переполнения или потери знака. И во-вторых, преобразование не приводит к исключительной ситуации. Если же неявное преобразование не удовлетворяет этим двум условиям, то следует выбрать явное преобразование. Рекомендации и ограничения по перегрузке операторов слескегт вазеол вв секасгк куреог пее опсьескес Несмотря нато что оператор приведения П нельзя перегружать явным образом, имеется все же возможность создать упоминавшиеся ранее операторы преобразования, выполняющие ту же самую функцию. Действие перегружаемого оператора распространяется на класс, для которого он определяется, и никак не связано с его первоначвльным применением к данным встроенных в СГГ типов.
Но ради сохранения ясности структуры и удобочитаемости исходного кода перегружаемый оператор должен, по возможности, отражать основную суть своего первоначального назначения. Например, назначение оператора + для класса тгггееп по сути не должно заметно отличаться от его назначения для целочисленных типов данных. Если, например, определить оператор + относительно некоторого класса таким образом, чтобы по своему действию он стал больше похожим на оператор /, то вряд ли от этого было бы много проку Главный принцип перегрузки операторов заключается в следующем: несмотря на то, что перегружаемый оператор может получить любое назначение, ради ясности новое его назначение должно быть так или иначе связано с его первоначальным назначением.
На перегрузку операторов накладывается ряд ограничений. В частности, нельзя изменять приоритет любого оператора или количество операндов, которое требуется для оператора, хотя в операторном методе можно и проигнорировать операнд. Кроме того, имеется ряд операторов, которые нельзя перегружать. А самое главное, что перегрузке не подлежит ни один из операторов присваивания, в том числе и составные, как, например, оператор +=. Ниже перечислены операторы, которые нельзя перегружать. Среди них имеются и такие операторы, которые будут рассматриваться далее в этой книге. 284 Часть !, язык Сз Ограничение, связанное с тем, что некоторые операторы, например ь=, нельзя перегружать, на самом деле не является таким уж непреодолимым.
Вообще говоря, если оператор определен как перегружаемый и используется в составном операторе присваивания, то обычно вызывается метод этого перегружаемого оператора. Следовательно, при обращении к оператору т= в программе автоматически вызывается заранее объявленный вариант метода орегасог+ () . Например, в приведенном ниже фрагменте кода метод орегасогь () автоматически вызывается для класса тьгее)), а в итоге объект Ь будет содержать координаты 11, 12, 13. Тьгееп а = оен тьгеев(2, 2, 3) К тьгеев Ь = оен тьгееп()О, та, )О)г Ь += а; У/ сложить координаты точек а и Ь И последнее замечание: несмотря на то, что оператор индексации массива (! нельзя перегружать с помощью операторного метода, имеется возможность создать индексаторы, о которых речь пойдет в следующей главе. Еще один пример перегрузки операторов Во всех предыдущих примерах программ, представленных в этой главе, для демонстрации перегрузки операторов использовался класс Тьгееп, и этой цели он служил исправно.
Но прежде чем завершить эту главу, было бы уместно рассмотреть еще один пример перегрузки операторов. Общие принципы перегрузки операторов остаются неизменными независимо от применяемого класса, тем не менее, в рассматриваемом ниже примере наглядно демонстрируются сильные стороны такой перегрузки, особенно если это касается расширяемости типов. В данном примере разрабатывается 4-разрядный целочисленный тип данных и для него определяется ряд операций. Вам, вероятно, известно, что на ранней стадии развития вычислительной техники тип данных широко применялся для обозначения 4-разрядных двоичных величин, называвшихся полубайтами, поскольку они составляли половину байта, содержали одну шестнадцатеричную цифру и были удобны для ввода кода полубайтами с пульта ЭВМ, что в те времена считалось привычным занятием для программистов! В наше время 4-разрядный тип данных применяется редко, но он по-прежнему является любопытным дополнением целочисленных типов данных в СФ.