Г. Шилдт - С#4.0 Полное руководство (1160795), страница 63
Текст из файла (страница 63)
А поскольку переменные Х1г(СЬ и Не1оЬС теперь являются закрытыми, то они доступны только для других членов своего класса, но не для членов производных классов. ПРИМЕЧАНИЕ Закрытый член класса остается закрытым в своем классе. Он не доступен из кода за пределами своего класса, включая и производные классы. 334 Часть ). Язык б№ На первый взгляд, ограничение на доступ к частным членам базового класса из производного класса кажется трудно преодолимым, поскольку оно не дает во многих случаях возможности пользоваться частными членами этого класса.
Но на самом деле это не так. Для преодоления данного ограничения в С(( предусмотрены разные способы. Один из них состоит в использовании защищенных (ргосессеб) членов класса, рассматриваемых в следующем разделе, а второй — в применении открытых свойств для доступа к закрытым данным. Как пояснялось в предыдущей главе, свойство позволяет управлять доступом к переменной экземпляра. Например, с помощью свойс?ва можно ввести ограничения на доступ к значению переменной или же сделать ее доступной только для чтения. Так, если сделать свойство открытым, но объявить его базовую переменную закрытой, то этим свойством можно будет воспользоваться в производном классе, но нельзя будет получить непосредственный доступ к его базовой закрытой переменной.
Ниже приведен вариант класса ТноОЯЬаре, в котором переменные Х1бЬЬ и Не1дпс превращены в свойства. По ходу дела в этом классе выполняется проверка: являютог ли положительными значения свойств И1бсЬ и не ьдЬь. Зто дает, например, возможносп указывать свойства и1бьь и не1дьс в качестве координат формы в любом квадранте прямоугольной системы координат, не получая заранее их абсолютные значения. Использовать открытые свойства для установки и // получения значений закрытых членов класса.
ия1пд Яуясеш? // Класс для двумерных объектов. с1аяя ТноОЯЬаре ( боиЬ1е ргт нтбгЬ| // теперь зто закрытая переменная боиьте ргь ЬеьдЬГ; // теперь зто закрытая переменная // Свойства ширины и высоты двумерного объекта. риЬ11с боиЬ1е ХьбгЬ ( дег ( гегигп ргт нгбгЬ? яег ( ргт нтбгЬ = ча1ие < О ? -ча1ие : ча1ие? ) ) риЬ1гс боиЬ1е Не1дЬГ ( дег ( гесигп рг1 ьеьдьсг яег ( рг1 Ье1дЬГ = ча1ие < О ? -ча1ие : ча1ие; ) ) риЬ11с чо1б 5Ьонотш() [ Сопяо1е.нгьяеътпе("Ширина и высота равны игбгь ь " и " + негдлг); ) ) Класс для треугольников, производный от // класса ТноОЯЬаре. с1аяя Тггапд1е : Тноп5Ьаре ( риьттс ясгьпд Ясу1ег // тип треугольника Глава 11. Васледованне 335 Возвратить площадь треугольника.
риЫ1с СонЫе Агеа() ( гетогп И1оСЬ * НегэЬС / 2у ) !/ Показать тип треугольника. роЫТс чоьб БЬонБСу1е() Сопяо1е.нгьпевьпе("треугольник " + БСу1е); ) ) с1авя БЬарея2 ( ясасгс чога Маьп() ( Тггапд1е С1 = лен Тг1ап01е() Тг1ап01е С2 = пем Тгьап01е() С1.ИТНСЬ = 4.0; С1.не10ЬС = 4.0; С.1.БСу1е = "равнобедренный"; С2.И1НСЬ = В.О~ С2.НеТПЬС = 12.0; с2.Бсу1е = "прямоугольный"; Сопяо1е.нгьоевьпе("Сведения об объекте С1: ")) С1.ЯЬонБСу1е(): С1. ЯЬонвьщ () ~ сопво1е.игьсеььпе("площадь равна " + с1.Агеа()); Сопяо1е.нгьтеььпе (); Сопяо1е.нг1Севьпе("Сведения об объекте С2: "); С2.БЬомБСу1е(); г2.
БЬоногт () ) Сопяо1е.нг(СеЬТпе("Площадь равна " + С2.Агеа()); В этом варианте свойства И1с)СЬ и Не1дЬС предоставляют доступ к закрытым членам ргт ытс)СЬ и ргт ЬетсЬС класса ТыоПБЬаре, в которых фактически хранятся значения ширины и высоты двумерного объекта. Следовательно, значения членов рг1 И1с(сь и рг1 ье1сьс класса тыопзьаре могут быть установлены и получены с помощью соответствующих о~крытых свойств, несмотря на то, что сами эти члены по- прежнему остаются закрытыми. Базовый и производный классы иногда еще называют суперклассом и иогТклассом соответственно.
Эти термины происходят из практики программирования на (ача. То, что в )ача называется суперклассом, в С№ обозначается как базовый класс. А то, что в )ача называется подклассом, в С№ обозначается как производный класс. Оба ряда терминов часто применяются к классу в обоих языках программирования, но в этой книге по-прежнему употребляются общепринятые в С№ термины базового и производного классов, которые принято употреблять и в С++. 336 Часть ). язык СФ Организация защищенного доступа Как пояснялось выше, открытый член базового класса недоступен для производного класса. Из этого можно предположить, что для доступа к некоторому члену базового класса из производного класса этот член необходимо сделать открытым.
Но если сделать член класса открытым, то он станет доступным для всего кода, что далеко не всегда желательно. Правда, упомянутое предположение верно лишь отчасти, поскольку в С() допускается создание зад(ни(як ного члена класса. Защищенный член является открытым в пределах иерархии классов, но закрытым за пределами этой иерархии. Защищенный член создается с помощью модификатора доступа ргосессес).
Если член класса объявляется как ргосессеб, он становится закрытым, но за исключением одного случая, когда защищенный член наследуется. В этом случае защищенный член базового класса становится защищенным членом производного класса, а значит, доступным для производного класса. Таким образом, используя модификатор доступа ргогесгес), можно создать члены класса, являющиеся закрытыми для своего класса, но все же наследуемыми и доступными для производного класса. Ниже приведен простой пример применения модификатора доступа ргосессес). г'/ Продемонстрировать применение модификатора доступа ргогесгес).
пв1пд Бувсеьо с1авв В ртосессег) ьпг 1, З; г'г' члены, закрытые длн класса Б, но доступные длн класса О риЫьс чо1о Яег(1пс а, 1пг Ы ( 1=а; з =Ь; ) рпЫго чотг) БЬон () ( сопяо1е.хтьсеььпе(1 + " " + З)г ) с1аяя О : В ( 1пс )и г'г' закрытый член члены 1 и 1 класса В доступны длл класса О риЫьс чоьо Яегх() Х = 1 * ) роЫьс чо1с( БЬонк() ( Сопяо1е.нгтееъьпе(Х)г )' ) с1аяв Ргогесгег)оеио ( вгагзо чоье Ма1п() ( О оЬ = пен О(); оЬ.ЯеС(2, 3)г гг' допустимо, поскольку доступно длн класса О Глава 11. Наследование 337 ЬЬ.ЗЛьм)); !/ допустимо, поскольку доступно для класса О ОЬ.Зебх)); // допустимо, поскольку входит в класс О оЬ.ЯЛоня)); !/ допустимо, поскольку входит в класс О ) В данном примере класс В наследуется классом Р, а его члены 1 и 1 объявлены как рго се с Лес), и поэтому они доступны для метода Яегх () .
Если бы члены 1 и 1 класса В были объявлены как р гьуа Ое, то они оказались бы недоступными для класса Р, и приведенный выше код нельзя было бы скомпилировать. Аналогично состоянию риЬ11с и ргьуасе, состояние ргогессес) сохраняется за членом класса независимо от количества уровней наследования. Поэтому когда производный класс используется в качестве базового для другого производного класса, любой защищенный член исходного базового класса, наследуемый первым производным классом, наследуется как защищенный и вторым производным классом. Несмотря на всю свою полезность, защищенный доступ пригоден далеко не для всех ситуаций. Так, в классе тмоРБЬаре из приведенного ранее примера требовалось, чтобы значения его членов Игг)ГЛ и НегоЬЬ были доступными открыто, поскольку нужно было управлять значениями, которые им присваивалисгь что было бы невозможно, если бы они были объявлены как ргосесгег).
В данном случае более подходящим решением оказалось применение свойств, чтобы управлять доступом, а не предотвращать его. Таким образом, модификатор доступа ргосессес) следует применять в том случае, если требуется создать член класса, доступный для всей иерархии классов, но для остального кода он должен быть закрытым. А для управления доступом к значению члена класса лучше воспользоваться свойством.
Конструкторы и наследование В иерархии классов допускается, чтобы у базовых и производных классов были свои собственные конструкторы. В связи с этим возникает следующий резонный вопрос какой конструктор отвечает за построение объекта производного класса: конструктор базового класса, конструктор производного класса или же оба? На этот вопрос можно ответить так: конструктор базового класса конструирует базовую часть объекта, а конструктор производного класса — производную часть этого объекта. И в этом есть своя логика, поскольку базовому классу неизвестны и недоступны любые элементы производного класса, а значит, их конструирование должно происходить раздельно. В приведенных выше примерах данный вопрос не возникал, поскольку они опирались на автоматическое создание конструкторов, используемых в С)) по умолчанию.
Но на практике конструкторы определяются в большинстве классов. Ниже будет показано, каким образом разрешается подобная ситуация. Если конструктор определен только в производном классе, то все происходит очень просто: конструируется объект производного класса, а базовая часть объекта автоматически конструируется его конструктором, используемым по умолчанию. В качестве примера ниже приведен переработанный вариант класса тг1ало1е, в котором определяется конструктор, а член Ясу1е делается закрытым, так как теперь он устанавливаетсяконсгруктором.
1)обавнть конструктор в класа тгьалд1е. ильлд Зулгеьм 338 Часть ). Язык С() // Класс для двумерных объектов. с1аяя ТноПЯЬаре ( <)оцЫе ргз н1НСЬ) НоцЫе рг1 Ьездьг) // Свойства ширины и длины объекта. рцЬ11с доцЫе И1НСЬ ( де1 ( гесигп ргя н1НГЬ! ) яес ( рг1 н1НСЬ = ча1це < 0 ? -ча1це : ча1це? ) рцЬ11с НоцЬ1е Не1дЬС ( дев ( геяцгп ргъ Ье1дЬГ) яея ( ргз Ье1дЬГ = ча1ие < 0 ? -ча1ие . "ча1це; ] ) рпЫ1с чоъа ЯЬонаьш() Сопяо1е.Игзсеьзпе (" Ширина и длина равны и1бгЬ + " и " ь нездьг)? ) // Класс для треугольников, производный от класса ТноПБЬаре.
с1аяя Тг1апд1е : ТноПБЬаре ( ягг1пд Ягу1е; // Конструктор. рпЬ11с Тгзапд1е(яягзпд я, НоцЫе н, НоцЬ1е Ь) ( И1бгЬ = и; // инициализировать член базового класса НеьдЬГ = Ь; // инициализировать член базового класса Бяу1е = я) // инициализировать член производного класса ) // Возвратить площадь треугольника. рцЬ11с НоцЬ1е Агеа() ( гесогп ИзсГЬ * Негдьс / 2; // Показать тип треугольника. рцЫ1с чо16 БЬонЯГу1е() ( Сопяо1е.нгьяеьзпе("Треугольник " + ясу1е) ) ) с1аяя БЬареяз ( яяаязс човб Мазп() ( Тгзапд1е Г1 = пен Тг1апд1е("равнобедренный", 4.0, 4.0)? Тгяапд1е С2 = пен Тг1апд1е("прямоугольный", 8.0, 12.0); Сопяо1е.кг1яеььпе("Сведения об объекте Г1: "); Г1.БЬонзсу1е()? Ь1.ЯЬонаьш()? сопяо1е.игягепгпе("площадь равна " т г1.лгеа())) Глава 11.