Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 59
Текст из файла (страница 59)
д. представляет конкретное воплощение математической концепции вещественного числа. Класс — это определяемый пользоватслем тип, Мы создаем новые типы для определения концепции, не выражаемой непосредственно встроенными типами. Например, мы моглп бы ввести тпп Тгипи Бпе (междугородняя линия) в программе, имеющей отноп|сние к телефонии, тип Ехр(оиоп (взрыв) в видеоигре или тнп !(в(<рптцгпрй» (сппсок абзацев) в программе обработки текста. Программы, тщгы в которых близко соответствуют концепциям приложения, обычно легче понимать и модифицировать. Тщательно подобранный набор типов, определяемых пользователем, делает программу более краткой и выразительной. Кроме того, такие типы дают возможность проведение разнообразного анализа кода. Б частности, они позволяют компилятору обнаружить случаи недопустимого использования объектов, которые в противном случае не были бы выявлены вплоть до этапа тестирования.
Ос нонной смысл введения новых типов состоит в разделении малозначащих деталей реализации (напримср, расположение в памяти составных частей объектов данного типа) от свойств, имеющих определяющее значение для правильного использования Глава 10 Классы 2?О сусцностн (например, полный набор функций доступа к данным).
1!одобное разделение лучше все~о выражается в терминах ограничения доступа к данным извне п использования для этой цели специальных процедур в рамках четко определенно~о интерфейса. В этой главе особое внимание уделяется относительно простым сконкретныы» типам, определяемым пользователем, которые с логической точки зрения незначительно отлпчасотся от встроенных типов. В идеале, такие типы должны отличаться от встроенных не по методам их использования, а только способом создания. 10.2. Классы Ллат — это определяемый пользователем тип. В ланном разделе изложены основные средства определения класса, создания объектов класса и манипулирования такнмп объектами. 10.2Л.
Функции-члены Рассмотрим реализацию концепции да! ы с использованием структуры Ра1е. Эта структура представляет дату и содержит набор функций, осуществляюгцнх манипу- ляции с псремепнымп. датами.' 1(предстовяенпе е!гис1 1!а!е ( и!с(, т, у, воЫ ии! да!е ((Эа!ей д, тс, спг,!и!) иоЫадс1 ! еагуза!ейд, !и!и) иоЫасЫ топЯ Да!ейд, !пел(, ооЫ аде! дау(?за!ейд, ипп(, Здесь нет явной связи меясду типом данных и функциями. Такую связь можно уста- новить, объявив функции в качестве членов структуры: вн ис! 1Эа!е ( !а!д, т, у, Функцнсь объявленные внутри определения класса (структура является одним из видов класса; э( ! 0.2.8) называются функциялпс- !ленами и их можно вызывать только для переменной соответствусосцего типа, используя стандартный синтаксис доступа к членаь! структуры.
Например: с!а!е ту Ь1Мн1ау, ооЫту дп1е йп1дд !и! тт сп! уу( ооЫпсЫ ! еаг(1п!п~, ооЫпдд топ!б (!а! п) иоЫ адд с1а у (сп! и) ), ооиЩ ( .Ра!е !одау, !одау сш! (1б, 10, 1996(, ту Ыг!Идпу тй 'ь!О, 12, 1950(, 1! инс!ц!сил!свис!ия д О придав!спсь и яет к д О ирибавтпь и л!есяцев к с! ," ,прноивить и днев к с1 ?сснницивлиэт(нэ сссс прибавить и лет ?сс прибавтпь и месяцев (~ приоавить и днеи 271 10.2. Классы Ра1е 1оспог ощ = 1вйау, 1отоггот,айй йау )!), Так как различные структуры могут иметь функции-члены с одинаковыми именами, при определении функции-члена мычсолжны указать имя структуры: ио!йРа1е !п!1)!п! сЫ, И1спт, !псуу) с(=сИ, т=тпс; у = уу, 10.2.2. Управление доступом Объявление Ра1е нз предыдущего раздела предоставляет набор функций для работы с Ра(е. Однако оно не указывает, что только эти функции непосредственно зависят ос представления Ра1е и только они могчт непосредственно осуществлять доступ к объектам класса Ра1е.
Зтсс ограничения можно отразить, воспользовавпшсь ключевым с.ловом с(авв вместо вйисб и!авв Ра1в ( сп1й,пс у' риЫИ во!й спл с1п1е ,'сп1 сЫ, !и1тт, т1 уу); // сснсссСсссслссвас)сся сссс прссОсссзсссссв и лтсс сс,С прииввиспн п.чесвцвв 11 ссрсссзивсссссь и днес! во!йайй уеаг)И! п), во!йайй топЯ )сп1п), стИасИ йпу)!ссссс), ), Метка рийсс разделяет тело класса на две части. Иьсепа в первой (рг!ива!в — закрытой) части могут использоваться только функциями-членами. Вторая (риЫИ вЂ” открытая) часть образует открытый интерфейс обьсктов класса. Структура, на самом деле, является классом, члены которого открыты по умолчаншо (б 10.2.8); функпипчлены можно определить точно также, как н выше. Например: В теле функции-члена имена членов (этого же класса) можно использовать без явного указания объекта.
В данном случае нмя относится к члену того обьекта, для которого вызвана функция. Например, если Ра1е:йп!1 () вызывается для 1ос(ау, т=тт означает 1ойау.т=тт, если Ра1е:йп!1 )) вызывается для ту Ьи Ьйау, т=тт означает ту Ьсгпсйау.т=тт. срункссия-члесс класса всегда взнаеть для какого обьекса она вызвана. Конструкцсся с!аввХ( ..), называется опрвдвлениеэл класса, потому что она определяет навьи| тнп. Но историческим причинам определение класса часто называют обвлвлвниееп класса. Также как н объявления, не являющиеся определениями. определение класса к!ожет быть повторено в другом исходном файле при помощи !)!пс!ийе, не нарушая прп этом правила одного определения ООсх (ч с).2,8).
Глава 10. Классы 272 гп(гпе иогг7Ваге: аг(гг уеаг 1гпг п1 уь=п; Однако функции, не являющиеся членами класса, не могут осуществлять непосред- ствецный доступ к закрытым членам. Например; ооЫ ггпгевяггр 11годегл гй г(у = 200;,гг оншбкш ггереленния Вогену является зокрыгпой Ограничение доступа к структуре данных явно обьявленным списком функций имеет несколько преимуществ. Например, ошибка, в результате которой гяаге приняла неверное значение (скажем, 36 декабря 1985 года), может быть вызвана только ггеверным кодом соответствующей функции-члена.
Из этого следует, что первая стадия отладки — локализация ошибки — может быть завершена еще до запуска программы. Это является частным случаем общего подхода, состоящего в том, что любое изменение поведения типа яяа(е может и должно влиять на его члены. В частности, если мы изменим представление класса, нам потребуется только изменить фуггкцигл-члены, чтобы воспользоваться новым представлением.
Код пользователя непосредственно зависит только от открьпого интерфейса, и код не потребуется переписывать (хотя может понадобиться его перекомпиляппя). Другое преимущество состоит в том, что для того чтобы научиться пользоваться классом, его потенциальному пользователю потребуется ознакомиться только с определениями функций-членов. Защита закрытых данных базируется на ограничении использования имен членов класса. Эту защиту можно обойти манипулированием с адресами и путем явного преобразования типа.
Но это, конечно, уже жульничество. С ьэ защищает от случайного, а не умышленного нарушения правил. Защиту против злонамеренного доступа к закрьпым данным в языке высокого уровня можно осуществить только на аппаратном уровне, и даже это является довольно сложной задачей в реальной системе. Фуггкггггя гпгг (1 добавлена отчасти потому, что обычно полезно иметь функцию, присвапвающую объекту значение, п отчасти потому, что ввиду закрытости данных, мы вынуждены ввести такую функцию. 10.2.3. Конструкторы Использование функций типа гпг1 1) ддя инициализации объектов класса неэлегантно и подвержено ошибкам.
Так как нигде яе сказано, что объект должен быть проинициализирован, программист может забыть об этом, или сделать это дважды (часто с одинаково разрушительными последствиями). Лучпшм подходом будет предоставление прог раммисту возможности объявить функцию, имеющую явное назначение — инициализация объектов. Бвидутого,чтотакаяфункция создаст(конструирует)значенияданноготипа, она называется конструкпгорояь Конструктор распознается по имени, которое совпадает с именем с амого класса. 11апример: 273 10.2.
Классы С!авя Ваге ( 0- Ва1е (!п1 сл1, !п1); ,Ч конструктор Если класс имеет конструктор, все об ьекты зтого класса будут пропнпциализирова- ны. Если конструктору требуются аргументы, они должны быть предоставлены: Васе содау = Расе (2,У, б, !9ВЗ) Ваге хтая (25, !2, !990), Ва1е ту ЬггЯдау, Ва1еге1еаве! 0(!О, !2), /! сокращенная форма )! ошибки; отсутствует ннициализацня О ошибка: отсутлтлвует п~репиш' аргумент Довольно часто удобно иметь несколько способов пнипиализапин объекта класса. Этого можно добиться, введя несколько конструкторов.
Например: О день, месяц, год 0 день, месяц, текущий год Ч день, текущие месяу( и год адата наумов«оною — сегодня !! дата в строковом предаповлении Конструкторы подчиняются тем же правилам разрешения перегрузки, что и осталь- ные функции Я 7.4). Пока конструкторы значительно различаются тнпами своих ар- гументов, компилятор может выбрать нужный в каждом конкретном случае: Ва 1е 1ос1ау (4); Рпсе!и!у4('Хи!у4, !983 ); Ва1е ди у ("5%по'); Васе пот, Размножение конструкторов в примере Ва1е является типичным, Во время проектирования класса у программиста постоянно возникает искушение добавить средства просто потому, что они кому-нибудь могут понадобиться.
Требуется основательно подумать, какие средства нужны на самом деле, и включать только их. Эти дополнительные интеллектуа льные усилия приводят к программам, которые более понятны и имеют лсеньший размер. Одним из способон уменыпения количества похожих друг на друга функций является использование аргументов по умолчанию (~ 7.5). В Ва1е каждому аргументу может быть присвоено значение по умолчаньпо, которое можно определить словами: «используй соответствующее значение из текущей латы>.
С1аев Ваге ( !л14, т, у; риЫ1с Васе (тсс!0=0, !пгтт=у, !л1уу=у), 0 ... ); с1аевВа1е( !лгу, т, у; риб!1с: с'с'.- Ва1е (т1, сп1, т1) Ва1е (1п1, !л1); Ва1е йл1); Ва1е (), Ваге (сопв1с!са~ ); О инициализация лоумоянанию текущей даспоа Глава ! О. Классы 274 Ра1е. Ра1е (т1йй, !пгтт, !п1уу( ( й = йй 2 йй: 1ойау.й; т = ттзтт гос!ау.т; у=уу Зуд 1ойауу, // проверка гпоео, ито Риге юиеет йолуепии!ое зни пение Когда значение аргумента используется для указания «вставить значение по умолчанп!о», оно должно находиться вне пределов допустимых «обычных» значений этого арсумента. Для йау и топ1й это требование очевидно выполняется сони не могут быть равны нулю), но для деаг возникают сомнения.