Г. Шилдт - С#4.0 Полное руководство (1160795), страница 40
Текст из файла (страница 40)
° Методы, получающие и устанавливающие значения закрытых данных, должны быть открытыми. ° Переменные экземпляра допускается делать открытыми лишь в том случае, если нет никаких оснований для того, чтобы они были закрытыми. Разумеется, существует немало ситуаций, на которые приведенные выше принципы не распространяются, а в особых случаях один или несколько этих принципов могут вообще нарушаться. Но в целом, следуя этим правилам, вы сможете создавать объекты, устойчивые к попыткам неправильного их использования. Практический пример организации управления доступом Для чтобы стали понятнее особенности внутреннего механизма управления доступом, обратимся к конкретному примеру.
Одним из самых характерных примеров объектно-ориентированного программирования служит класс, реализующий сглек— структуру данных, воплощающую магазинный список, действующий по принципу "первым пришел — последним обслужен". Свое название он получил по аналогии со стопкой тарелок, стоящих на столе.
Первая тарелка в стопке является в то же время последней использовавшейся тарелкой. Стек служит классическим примером объектно-ориентированного программированил потому, что он сочетает в себе средства хранения информации с методами доступа к ней. Для реализации такого сочетания отлично подходит класс, в котором члены, обеспечивающие хранение информации в стеке, должны быть закрытгами, а методы доступа к ним — открытыми. Благодаря инкапсуляции базовых средств хранения информации соблюдается определенный порядок доступа к отдельным элементам стека из кода, в котором он используется. Длл стека определены две основные операции: ипместилчь данные в стек и извлечь их оттуда.
Первая операция помещает значение на вершину стека, а вторая — извлекает значение из вершины стека. Следовательно, операция извлечения является безвозвратной: как только значение извлекаетсл из стека, оно удаляется и уже недоступно в стеке. (вава 8. Подробнее о методах н вассах 213 В рассматриваемом здесь примере создается класс Всас)с, реализующий функции стека. В качестве базовых средств для хранения данных в стеке служит закрытый массив. А операции размещения и извлечения данных из стека доступны с помощью открытых методов класса 50асос Таким образом, открытые методы действуют по упомянутому выше принципу "последним пришел — первым обслужен".
Как следует из приведенного ниже кода, в классе 5гаск сохраняются символы, но тот же самый механизм может быть использован и для хранения данных любого другого типа. // Класс для хранения символов в стеке. цягпо 5уясещ) с1аяя зсаск ( // Эти члены класса являются закрытыми. спаг[] ягсхи // массив, содержащий стек 1пг гоя; О индекс вершины стека Построить пустой класс 5саск длн реализации стека заданного размера.
рцЬ11с зсасг(гпс я1ге) ясск = пен сЬаг[ягге]; // распределить память длн стека гоя = О," ) Поместить символы в стек. рцбьгс чо1С РцяЬ(сЬаг сп) ( 11(соя==явок.ьепсГЬ) Сопяо1е.иггсе11пе(" — Стек заполнен.") гегцгп; ядоя[соя] = сп; гояе+; ] // Извлечь символ из стека. рнЬ11с сЬаг Рор() 1г(соя==О) ( Сопяо1е.нггсеввпе(" — Стек пуст.") гесцгп (сЬаг) 0; ) гоя--; гесогп ягсг(соя]; ) Возвратить значение Ггце, если стек заполнен.
рпЬ1гс Ъоо1 1ярц11() ( гесцгп соя==явок.ЬепОГЬ) ) О Возвратить значение Ггце, если стек пуст. рцЬ1гс Ьоо1 тякшргу() ( гесцгп соя==0; 214 Часть 1. Язык С№ О Возвратить общую емкость стека. рцЫЬс Ьпг СарасЬСу() ( гесцгп ясак.ьепчсЫ ) Возвратить количество обьектов, находящихся в данный момент в стеке. рцЫгс Ьпг Сегицш() ( гесцгп соя; ) ) Рассмотрим класс эсаск более подробно. В начале этого класса объявляются две следующие переменные экземпляра. !/ Эти члены класаа являются закрытыми. с)тат() васям !/ массив, содержащий стек Ьпс совг индекс вершины стека Массив яСс)с предоставляет базовые средства для хранения данных в стеке (в данном случае — символов).
Обратите внимание на то, что память для этого массива не распределяется. Это делается в конструкторе класса ВСас)г. А член Соя данного класса содержит индекс вершины стека. Оба члена, соя и ясс)с, являются закрытыми, и благодаря этому соблюдается принцип "последним пришел — первым обслужен". Если же разрешить открытый доступ к члену ясах, то элементы стека окажутся доступными не по порядку.
Кроме того, член соя содержит индекс вершины стека, где находится первый обслуживаемый в стеке элемент, и поэтому манипулирование членом Соя в коде, находящемся за пределами класса ВСас)г, следует исключить, чтобы не допустить разрушение самого стека. Но в то же время члены ясс)г и соя доступны пользователю класса Всаск косвенным образом с помощью различных отрытых методов, описываемых ниже. Рассмотрим далее конструктор класса эсас)с. Построить пустой клаас Бгасх для реализации стека заданного размера.
рцЫЬс БСаск(апг втге) ягс)г = пен с)|аг(ягге)Г // распределить память для стека соя = О; ) Этому конструктору передается требуемый размер стека. Он распределяет память для базового массива и усганавливает значение переменной соя в нуль. Следовательно, нулевое значение переменной Соя указывает на то, что стек пуст. Открытый метод Рцяб () помещает конкретный элемент в стек, как показано ниже. Поместить символы в стек. рцЫЬс чоай Рцв)т(с(тат сь) ( ЬГ(соя==асс)г.ьепэс)1) ( сопво1е.игасеътпе(" — стек заполнен.") гесцгпг ) васс(сов) = сЫ сов++; ) Глава 8.
Подробнее о методах и классах 215 Элемент, помещаемый в стек, передается данному методу в качестве параметра сЬ. Перед тем как поместить элемент в стек, выполняется проверка на наличие свободного места в базовом массиве, а именно: не превышает ли значение переменной соя длину массива я Сои. Если свободное место в массиве я с си есть, то элемент сохраняется в нем по индексу, хранящемуся в переменной соя, после чего значение этой переменной ннкрементируется. Таким образом, в переменной соя всегда хранится индекс следующего свободного элемента массива яссх.
Для извлечения элемента из стека вызывается открытый метод Рор (), приведенный ниже. Извлечь символ из стека. роЬ1гс сЬаг Рор() ( гв(соя==с) ( Сопяо1е.иг1геьгпе(" — Стек пуст.")," гесогп (слаг) О; ) гоя--; гегчгп ягсХ[гоз) В этом методе сначала проверяется значение переменной соя. Если оно равно нулю, значит, стек пуст.
В противном случае значение переменной соя декрементируется, и затем из стека возвращается элемент по указанному индексу. Несмотря на то что для реализации стека достаточно методов Ризи () и Рор (), полезными могут оказаться и другие методы. Поэтому в классе Бсасх определены еще четыре метода: 1ярп11 (), 1яЕщрбу(), Сарас1су() и Себишп () .
Эти методы предоставляют всю необходимую информацию о состоянии стека и приведены ниже. // Возвратить значение Ггое, если стек заполнен. риЬ11с Ьоо1 1яг"о11() ( гесогп соя==явок.ьепчслт ) // Возвратить значение Ггое, если стек пуст. рпЬ11с Ьоо1 1яящргу() ( гесогп соя==с; ) // Возвратить общую емкость стека. роЬ11с 1пс Сарасггу() ( гесогп яссХ.Ьепослт ) // Возвратить количество объектов, находящихся в данный момент в стеке.
роЬ1гс гпо Весиою() ( гегпгп гоя! ) Метод 1ярп11 () возвращает логическое значение стае, если стек заполнен, а иначе — логическое значение га1яе. Метод 1яещрсу () возвращает логическое значение С гпе, если стек пуст, а иначе — логическое значение Га1 яе. Для получения общей емкости стека (т.е. общего числа элементов, которые могут в нем храниться) достаточно 216 Часть!. Язык С() вызвать метод Сарасзту (), а для получения количества элементов, хранящихся в настоящий момент в стеке, — метод Петнцш () . Польза этих методов состоит в том, что для получения информации, которую они предоставляют, требуется доступ к закрытой переменной соя.
Кроме того, они служат наглядными примерами организации безопасного доступа к закрытым членам класса с помощью открытых методов. Конкретное применение класса Втаск для реализации стека демонстрируется в приведенной ниже программе. // продемонстрировать применение класаа згаск. цяьпд Вуясеш; Класс для хранения символов в стеке. с1аяя Зсасх ( Эти члены класса являются закрытыми. сьаг() ягсум // массив, содержаший стек 1па Гоя; индекс вершины стека Построить пустой класс Зааск для реализации стека заданного размера.
рцЫьс Зсасх(ьпа яьге) ( ясах = пен сьаг(яьге)у // распределить память для стека гоя = О; ) Поместить символы в стек. рцЬ1ьа того Рцяв(аЬаг аЫ ( 11(аоя==яссХ.ЬепЧГЫ ( Сопяо1е.иг1аеЬьпе(" — Стек заполнен.") гегцгп; ) васк(гоя) = аЬ~ саяною ) Извлечь символ из стека. рцЫьс сЬаг Рор() ( 1Г[соя==О) ( Сопяо1е.нг1аеЬ1пе(" — Стек пуст."); геацгп (сЬаг) О; гоя —; гегцгп ясах(гоя) ) Возвратить значение Ггце, если стек заполнен. рцЫ1с Ьоо1 1ягц11() ( гетцгп Соя==ясак.ьепооЬЬ; ) // Возвратить значение Ггце, если стек пуст. рсЬ11а Ьоо1 гяЕшрау() ( Глава 8. Подробнее о методах н вассах 217 гегогп соя==о Возвратить общую емкость стека.
роЬ11с 1п'ь Серас1бу() ( гетега ясак.ьепоГЬ) ) Возвратить количество объектов, находящихся в данный момент в стеке. риЬ11с гпс Сеснощ() ( геспгп гоя) с1аяя Ясасабещо ( ясабас чогб Ма1п() ( ЯсасХ яСХ1 = пен Ясаск(10)) ЯсасХ яск2 = пен Ясаса(10)) ЯбасХ яСХЗ = пен Ябаса(10)) сбаг сЬ) 1пг 1) // поместить рлд символов в стек ягХ1. Сопяо1е.Хг1сетапе("Поместить символы А-0 в стек яСХ1.") Рог(1=0) !яГХ1.1ярп11()) 1++) ягх1.Рпяь((сьаг) ('А' + г))) 11(ягн1. 1яро11()) Сопяо1е.нгтсе11пе("Стек асн1 заполнен.") О Вывести содержимое стека яГХ1.
Сопяо1е.нггсе("Содержимое стека яГХ1: ") нЬг1е( !яГХ1.1явщрГУ() ) ( сЬ = ягХ1.Рор()) Сопяо1е.нгтсе(сЬ)) ) Сопяо1е.нг1сеьтпе() 11(яск1.1явюрсу()) Сопяо1е.нггсеаапе("Стек яГХ1 пуст. 1п")) // Поместить дополнительные символы в стек яск1. Сопяо1е.нг1се11пе("Вновь поместить символы А-0 в стек яГХ1.")) тот(1=0; !ягн1.1ярн11(); яГХ1.РояЬ((снаг) ('А' + 1))) А теперь извлечь элементы из стека яГХ1 и поместить их в стек яок2.
// В итоге элементы сохраняются в стеке яГХ2 в обратном порядке. Сопяо1е.Иггсеаапе("А теперь извлечь символы из стека ятк11п" + "и поместить их в стек ягх2.")) нЬг1е( !яок1.1явщрбу() ) ( сЬ = яСХ1.рор(); яГХ2.рпяЬ(сЬ)) 218 Часть 1. Язык С() Сопзо1е.иггсе("Содержимое стека вГХ2: ") ннг1е( )зсх2.1зкырсу() ) ( сь = згк2.рор()г Сопво1е.ыггге(сЬ)г ) Сопзо1е.ыггпеъгпе("1п")) // Поместить 5 символов в стек. Сопво1е.нггсеЬ1пе("Поместить 5 символов в стек вГКЗ.")) Гог(1=0; г < 5; 1++) вСКЗ.Рпзь((снаг] ('А' + г))) Сопво1е.иг1сеЬ1пе("Емкость стека зГКЗ: " + вГКЗ.Сарасгпу()) Сопво1е.иггсеьзпе("Количество объектов в стеке зСХЗ: ЗГХЗ.Пеьнчы () ) Г При выполнении этой программы получается следующий результат. Поместить символы А-д в стек зок1.
Стек вГХ1 заполнен. Содержимое стека зсн1: 31НПРЕПСВА Стек зск1 пуст. Вновь поместить символы А-3 в стек вок1. А теперь извлечь символы из стека зок1 и поместить их в стек вГХ2. Содержимое стека вгх2: АВСПЕРСН13 Поместить 5 символов в стек вГКЗ. Емкость стека вГКЗ: 10 Количество объектов в стеке зГХЗ: 5 Передача обьектов методам по ссылке В приведенных до сих пор примерах программ при указании параметров, передаваемых методам, использовались типы значений, например Епс или г)оиЬ1е. Но в методах можно также использовать параметры ссылочного типа, что не только правильно, но и весьма распространено в ООП. Подобным образом объекты могут передаваться методам по ссьглке.