Г. Шилдт - С#4.0 Полное руководство (1160795), страница 45
Текст из файла (страница 45)
Для того чтобы стало понятнее, как это делается, обратимся к конкретному примеру. В языках программирования, не поддерживающих перегрузку методов, каждому методу должно быть присвоено уникальное имя. Но в программировании зачастую возникает потребность реализовать по сути один и тот же метод для обработки разных типов данных. Допустим, что требуется функция, определяющая абсолютное значение. В языках, не поддерживающих перегрузку методов, обычно приходится создавать три или более вариантов такой функции с несколько отличающимися, но все же разными именами. Например, в С функция аЬв () 'возвращает абсолютное значение целого числа, функция 1аЬв () — абсолютное значение длинного целого числа, а функция йаЬз () — абсолютное значение числа с плавающей точкой обычной (одинарной) точности. В С перегрузка не поддерживается, и поэтому у каждой функции должно быль свое, особое имя, несмотря на то, что все упомянутые выше функции, по существу, делают одно и то же — определяют абсолютное значение.
Но это принципиально усложняет положение, поскольку приходится помнить имена всех трех функций, хотя они реализованы по одному и тому же основному принципу. Подобные затруднения в СФ не возникая>т, поскольку каждому методу, определяющему абсолютное значение, может быль присвоено одно и то же имя. И действительно, в состав библиотеки классов для среды .)х)ЕТ Ргашегчог)с входит метод )(ьв (), который перегружается в классе нуз сепг.
магь для обработки данных разных числовых типов. Компилятор С() сам определяет, какой именно вариант метода АЬз () следует вызывать, исходя из типа передаваемого аргумента. Главная ценность перегрузки заключается в том, что она обеспечивает доступ к связанным вместе методам по общему имени. Следовательно, имя )ГЬа обозначает общее выполняемое действие, а компилятор сам выбирает конкретный вариант метода по обстоятельствам. Благодаря полиморфизму несколько имен сводятся к одному. Несмотря на всю простоту рассматриваемого здесь примера, продемонстрированный в нем принцип полиморфизма можно расширить, чтобы выяснить, каким образом перегрузка помогает справляться с намного более сложными сизуациями в программировании. Когда метод перегружается, каждый его вариант может выполнять какое угодно действие.
Для установления взаимосвязи между перегружаемыми методами не существует какого-то одного правила, но с точки зрения правильного стиля программирования перегрузка методов подразумевает подобную взаимосвязь. Следовательно, использовать одно и то же имя для несвязанных друг с другом методов не следует, хотя это и возможно.
Например, имя эцг можно было бы выбрать для методов, возвращающих квадрат и квадратный корень числа с плавающей точкой. Но ведь это Глава В. Подробнее о методах и классах 241 принципиально разные операции. Такое применение перегрузки методов противоречит ее первоначальному назначению. На практике перегружать следует только тесно связанные операции. В С() определено понятие сигнатуры, обозначающее имя метода и список его параметров. Применительно к перегрузке это понятие означает, что в одном классе не должно существовать двух методов с одной и той же сигнатурой. Следует подчеркнуть, что в сигнатуру не входит тип возвращаемого значения, поскольку он не учитывается, когда компилятор С() принимает решение о перегрузке метода.
В сигнатуру не входит также модификатор рагап)я. Перегрузка конструкторов Как и методы, конструкторы также могут перегружаться. Это дает возможность конструировать объекты самыми разными способами. В качестве примера рассмотрим следующую программу. // Пропемонстрнрояать перегрузку конструктора. оя1пд Буясее) с1аяв МуС1аяя ( рагс 1пс х; роЫ1с МуС1аяя() ( Сопяо1е.Хгьяеьгпе("В конструкторе МуС1аяя().") х=с; ) роЫ1с Мус1аяя(гпс 1) ( Сопяо1е.Хгггесгпе("В конструкторе Мус1аяя(гпс) .") ) х = ).; ) роЫ1с МуС1аяя (г)оное г() Сопяо1е.Хг1севапе ("8 конструкторе Мус1аяя (оооЫе) .") х = (1пг) о; ) роЫ1с МуС1авя (тпс 1, ьпс 3) ( Сопяо1е.нг1сеъгпе("В конструкторе Мус1авв(1пс, 1пг).") х = 1 * 3) ) ) с1аяя Отег1оабсопясеыо ( ясасгс когц Ма1п()- ( Мус1аяя С1 = пен Мус1аяя() ) МуС1аяя Г2 = пен МуС1а*я(88) ) МуС1аяя Сз = пен Мус1аяя(17.23)) МуС1аяя Г4 = пен МуС1авя(2, 4); Сопяо1е.нг1геьгпе("г1,х: " + С1.х)) 242 Часть [.
Язык б№ Сопяо1е.игггеътпе("С2.х: " ь С2.х); Сопво1е.иггсеъгпе("сз.х: " + сз.х); Сопяо1е.иг1Секвпе("С4.х: " + С4.х); ) ) При выполнении этой программы получается следующий результат. В конструкторе МуС1авя() . В конструкторе МУС1аяя(1пС). В конструкторе МуС1аяя(бооЬ1е).
В конструкторе МУС1авя(1пС, 1пС). С1.х: О С2.х: 88 Сз.х: 17 с4.х: 8 В данном примере конструктор МуС1а за () перегружается четыре раза, всякий раз конструируя объект по-разному. Подходящий конструктор вьпывается каждый раз, исходя из аргументов, указываемых при выполнении оператора пеы. Перегрузка конструктора класса предоставляет пользователю этого класса дополнительные преимущества в конструировании объектов. Одна из самых распространенных причин для перегрузки конструкторов заключаетсл в необходимости предоставить возможность одним объектам инициализировать другие.
В качестве примера ниже приведен усовершенствованный вариант разработанного ранее класса ЯСасК, позволяющий конструировать один стек из другого. Класс для хранения символов в стеке. оягпч Яуясешг с1аяя БсасК ( // Эти члены класса являются закрытьяяи. сьаг[] яссК; // массив, содержащий стек гпс соя; // индекс вершины стека // Сконструировать пустой объект класса Бсасх по заданному размеру стека.
роътгс БСасК(1пг я1ге) ( ягсК = пен спаг[ягге]; // распределить память для стека соя = О; ) Сконструировать объект класса ЯСаск из сушествуюшего стека. росттс ЯСасК(ЯСасК ощ [ Распределить память для стека. ягсК = пен спас[оп.ягск.ъепоСЬ]; Скопировать элементы в новый стек. Еог(тпг 1=0~ 1 < оЬ.Соя~ 1ь+) ягсК[1] = оЬ.ягсх[1]; Установить переменную Сов для нового стека. соя = оЬ.сояг Глава 8.
Подробнее о методах н вассах 243 Поместить символы в стек. роЬ11с чохб РояЛ(спаг сЛ) ( ьа(соя==воск.ьепдГЛ) ( Сопяо1е.кг1ГеЬ1пе(" — Стек заполнен."); геоогп)— ) всех[соя) = с)м ояь+; // Извлечь символ иэ стека. роЬ11с спаг Рор() ( 1г(оса==о) ( Сопяо1е.нг1сеь1пе(" — Стек пуст.") гесогп (спаг) 0; ) Гоя--; гесогп воск[поз); ) // Возвратить значение Сгое, если стек заполнен.
роЬ11с Ьоо1 1яро11() ( гегогп соя==всех.лепдгпу ) Возвратить значение Ггче, если стек пуст. роЬ1гс Ьоо1 1яЕщрсу() ( гегогп соя==о; ) // Возвратить общую емкость стека. роЬ11с хпя Сарасхсу() ( гегогп ягсх,ьепцгл; ) // Возвратить количества объектов, находящихся в настоящий момент в стеке. роЬ11с 1пс Весыощ() ( геоогп гоя; ) ) // Продемонстрировать применение класса Ясаск. с1аяя ЯсаскОещо ясат1с тога Масп() ( Ягаск ягк1 = пен Ягаск(10) сЛаг сп| гпг // Поместить рлд символов в стек ягк1. Сопяо1е.иг1оеь1пе("Поместить символы Л-0 в стек яГХ1.")," Еог(1=0; !яГХ1.1ярп11(); г++) 244 Часть ].
Язык С() зСК1. Роек ( (сЬаг) ( 'А' + 1) ); УУ Создать копию стека вгсК1. БСасК зСК2 = пеи БгасК(вСК1); Вывести содержимое стека вСК1. Сопзо1е.игьге("Содержимое стека вСК1: ") иа11е( !вСК1.1вЕщргу() ) ( сп = вСК1.рор()Г Сопво1е.иг1Се(сп); ) Сопво1е.иг1СеЬтпе() Сопво1е.Иггге("Содержимое стека вСК2: ")г ип11е ( !вСК2.1вЕщргу() ) ( сп = вСК2.рор()г Сопво1е.иг1Се(сЬ)Г ) Сопзо1е.иг1сеЬ1пе ("Кп") г Результат выполнения этой программы приведен ниже. Поместить символы я-д в стек зСК1. Содержимое стека вСК1: 01НОРЕОСВй Содержимое стека вСК2: 01НВРЕПСВА В классе БсаскРето сначала конструируется первый стек (зск1), заполняемый символами.
Затем этот стек используется,для конструирования второго стека (зск2). Это приводит к выполнению следующего конструктора класса БСасК. // Сконструировать объект класса Бгасх из существующего стека. роЬ1гс БгасК(БСасК оЬ) ( Распределить память для стека. вгсК = пеи спаг[оЬ.вгсК.ЬепчСЬ]г /У Скопировать элементы в новый стек. тот(гпс 1=0~ г < ОЬ.сов~ 1++) всех[1] = оЬ.вссК[г]г /! Установить переменйую Сов для нового стека. соз = оЬ.созг ) В этом конструкторе сначала распределяется достаточный объем памяти для массива, чтобы хранить в нем элементы стека, передаваемого в качестве аргумента оЬ.
Затем содержимое массива, образующего стек оЬ, копируется в новый массив, после чего соответственно устанавливается переменная Соз, содержащая индекс вершины стека. По завершении работы конструктора новый и исходный стеки существуют как отдельные, хотя и одинаковые объекты. Глава 8. Подробнее о методах и классах 245 Вызов перегружаемого конструктора с помощью ключевого слова сЬл.в Когда приходится работать с перегружаемыми конструкторами, то иногда очень полезно предоставить возможность одному конструктору вызывать другой.
В С() это дается с помощью ключевого слова сьая. Ниже приведена общая форма такого вызова. иия конструктора(список параметров1) : ГЫз(список лараиетров2) ( Тело конструктора, которое может быть пустым. ) В исходном конструкторе сначала выполняется перегружаемый конструктор, список параметров которого соответствует критерию список параметров2, а затем все остальные операторы, если таковые имеются в исходном конструкторе.
Ниже приведен соответствующий пример. Продемонстрировать вызов конструктора с помощью ключевого слова свтз. избпч Яузгещ1 с1азя ХУСоогб ( рпЬ11с 1пс х, у; рпЫ1с ХУСоогб() : СЬгз(0, О) ( Сопзо1е.иггсевгпе("В конструкторе ХУСоогб()"); ) роЫТС ХУСоогб(ХУСоогб оЬЗ): ГЫз(оЬЗ.х, оЬЗ.у) ( Сопзо1е.игггевьпе("В конструкторе ХУСоогб(оЬЗ)"); ) рпЬ11с ХУСоогб(гпс 1, Тпс З) Сопяо1е.игггесгпе("В конструкторе ХУСоогб(лпг, Тпг)") х у=з ) ) с1азз Очег1оабсопзпещо ( зсаг1с тото Ма1п() ( ХУСоогб Г1 = пен ХУСоогб(); ХУСоогб С2 = пеи ХУСоогб(8, 9) ХУСоогб гз = пеи ХУСоогб(г2)1 Сопзо1е.игггеьгпе("С1.х, Г1.у: " + Г1.х + ", " + С1.у)) Сопзо1е.иг1севапе("Г2.х, Г.2.у: " + Г2.х + ", " ь Г2.у); Сопяо1е.игтгещпе("Сз.х, Сз.у: " з Сз.х ь ", " + Сз.у); ) Выполнение этого кода приводит к следующему результату.