Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 59
Текст из файла (страница 59)
Представлены методики объектно-ориентированного и обобщенного стилей программирования. Главы 10. Классы 11. Перегрузка операций 12. Наследование классов 13. Шаблоны 14. Обработка исключений 15. Иерархии классов ь... нет дела, коего устройство было бы труднее, ведение опаснее, а успех сомнитель- нее, нежели замена старых порядков новыми. Кто бы ни выступал с подобным на- чинанием, его ожидает враждебность тех, кому выгодны старые порядки, и холод- ность тех, кому выгодны новые ...» — Николо Макиавелли (»государь» рч) Классы Эти типы совсем не «абстрактные»; они столь оке реальны как тг или )гиок — Дуг МакИлрой Концепции и классы — члены класса — управление доступом — конструкторы— статические члены класса — копирование по умолчанию — константные функции-члены — гйе — структуры — определение функции в классе — конкретные классы — функции-члены и функции поддержки — перегруженные операции— использование конкретных классов — деструкторы — конструкторы по умолчанию — локальные переменные — копирование, определяемое пользователем— пеи и ае!его — классовые обьекты как члены класса — массивы — статическая память — временные объекты — объединения — советы — упражнения.
10.1. Введение Концепция классов языка С++ нацелена на то, чтобы снабдить программиста средствами создания новых типов данных, пользоваться которыми удобно в той же мере, что и встроенными типами. Кроме того, концепции производных классов (глава 12) и шаблонов (глава 13) позволяют программисту синтаксическим образом извлечь практическую выгоду из определенного рода отношений между классами. Тип есть конкретное представление концепции. Например, встроенный тип роаг вместе с операциями +, —, * и т.д, представляет собой конкретную (приближенную) реализацию математической концепции вещественных чисел.
Каасс — это тип, определяемый пользователем (программистом). Мы создаем новые типы, чтобы точнее отражать концепции, не представимые встроенными типами. Например, для телефонной программы может пригодиться тип лгипк 1(пе (магистральная междугородняя телефонная линия), для видеоигры — тип .Ехр(ое(оп (взрыв), а для текстового процессора — тип йзгсРагадгарй> (список параграфов, то есть абзацев).
Программы, в которых типы близки концепциям предметной области задачи, и понимать легче, и легче сопровождать (в том числе модифицировать) в течении всего срока их эксплуатации. Глава 10. Классы 284 10.2. Классы Класс — этот тип, определяемый пользователем. В данном разделе рассматриваются основные средства определения классов, создания объектов классов и манипулирования объектами классов. 10.2.1. Функции-члены Рассмотрим реализацию концепции даты с помошью структуры Ваге, содержащей данные, и набора функций для манипулирования переменными этого типа; // представление вггисг Ваге ( гпг гг, т, уг )г // инициализация г( //прибавить и лет к Н // прибавить и месяцев к гг // прибавить и дней к д еоЫ та ггаге(Вагеа гг, гпг, гиг, Ыг) г гоЫ агЫ уеаг (Вагеь г(, гпг п) г еоЫ агггг топггг (Вагеа гг, гпг и) г гоЫ огЫ ггпу(Вигов гг, гиг и) г Явной связи между структурой и набором функций нет.
Такую связь можно выразить явно, объявив функции в качестве членов структуры: зп исг Ваге ( гпгИ, гп, у; еоЫ гтг (гпг гЫ, тг тт, гпгуу) г иоЫ агЫ уеаг (ии и ) г еоЫ агЫ топггг (тг и); еоЫ агЫ ггпу(гп< и) г // инициализация //прибавить и лет //прибавить и месяцев //прибавить и дней Умело подобранный набор пользовательских типов делает программу более компактной и более выразительной.
Он также позволяет выполнять тщательный анализ кода. В частности, компилятор может при этом надежно выявлять случаи недопустимого использования объектов указанных типов, которые в противном случае не были бы выявлены иначе, как с помощью процесса массированной и длительной отладки. Фундаментальная идея, которой нужно следовать при построении нового типа, заключается в том, чтобы четко отделить случайные детали реализации (например, расположение в памяти отдельных частей объектов этого типа) от таких свойств, которые обеспечивают удобное и безошибочное использование типа в клиентском коде (например, полный набор функций для доступа к данным).
Подобное разделение лучше всего реализуется путем предоставления доступа к структурам данных и другим внутренним подробностям исключительно через специально предназначенный для этого интерфейс. В настоящей главе основное внимание фокусируется на относительно простых конкретных классах, которые с логической точки зрения слабо отличаются от встроенных типов. В идеале, такие типы должны отличаться от встроенных типов не в плане использования клиентским кодом, а только способами их создания. 10.2. Классы Функции, объявленные в теле определения класса (а структура это один из видов класса; з)0.2.8), называются функциями-членами (тетЬег1ипсгуопз)( и могут вызываться только от имени переменных этого типа, используя стандартный синтаксис доступа к членам структуры.
Например: Васе ту Ь(гЫау; »о(ау () ( Роге (одау; годау. и»11 (16, 10, 1990); ту Ь(заЫау. 1п1( (30, 12, 1950); Рагс готоггои = ии(ау) го»постои . адд Иау (1); 11 ... ) Так как различные структуры могут иметь функции-члены с одинаковыми именами, мы должны явно указывать имя структуры в определении функций-членов: »о1д лаге::ши(шг дд, шг тт, ш(уу) ( д = д«(; у =уу' ) В теле функций-членов имена членов класса можно использовать без явных ссылок на объект класса. Это означает, что имена ссылаются на поля объекта, для которого функция-член вызвана. К примеру, если функция-член Раге:: 1п11() вызвана для объекта годау, то т=тт означает годау.
т=тт, а если эта функция-член вызвана для объекта ту Ыг(Ыау, то т=тт означает ту Ыг(Ыау. т=тт. В процессе своей работы функция-член всегда «знает», для какого объекта она вызвана. Конструкция с1ш»Х (...)' называется определением класса (с(ат де))пг(гоп), так как с ее помощью определяется новый тип данных.
По историческим причинам часто определение класса называют объявлением класса (с(ат дес(ага((оп). Как и объявления, не являюшиеся определениями, определение класса может без нарушения правила одного определения (89.2.3) присутствовать в разных исходных файлах программы, будучи помешенным в них директивой ()1пс1иде. 10.2.2.
Управление режимом доступа Объявление Расе из предьшушего раздела предоставляет набор функций для манипулирования объектами этого типа. Однако оно не фиксирует того факта, что лишь зти функции знают точное устройство объектов типа и только через них осу- ! Постепенно, особенно в русскоязычной литературе, вместо термина «функция-член» все чаше применяется термин «метод класса» (англ. термин — с1ам гпеФод). — Прим. ред. 286 Глава 1 О. Классы ществляется прямой доступ к этим объектам. Чтобы выразить указанное ограниче- ние явным образом, применим ключевое слово с!ат вместо зггасг. с!ат Разе ( 1и1д, т, у; риЫгс: гоЫ 1ип(т! юЫ, 1пг тт, 1игууу) г гоЫ а~И уеаг'(1ие п); го1й агЫ тоигл (гпг и); гоЫ агЫ аау(1иг и); )' гу инициализация (г прибавить и лет ,(г прибавить и месяцев ,У прибавить п дней !п(те гоЫ Роге::агЫ уеаг(тг п) ( у ь= и; ) Обычные же функции (не функции-члены) теперь лишены возможности обращаться к закрытым полям класса напрямую.
Например: го1й бтетагр (Рагеь й) ( д.у -= 2ббг ) I1 еггог: Ра(е:: у имеет режим рпго(е Из ограничения доступа к внутренней структуре класса явно предназначенным для этого интерфейсом вытекает ряд преимуществ. Например, любая ошибка, приводящая к неверным данным в объекте типа Раге (например, 36 декабря 1985 года), является следствием неверного кода в одной из функций-членов. Это означает, что локализация ошибки (первая стадия отладки) может быть выполнена на ранней стадии разработки программы (чуть ли не до ее запуска).
Более общее положение состоит в том, что любое изменение поведения типа Роге может и должно осуществляться изменением кода функций-членов. Например, любое усовершенствование внутренней структуры данных класса требует лишь изменения функций-членов с тем, чтобы воспользоваться новыми возможностями. Код же клиента, опирающегося на открытый интерфейс класса, остается при этом неизменным (может потребоваться его перекомпиляция). Еще одно достоинство состоит в том, что потенциальному пользователю класса для начала работы с новым типом данных нужно изучить лишь работу с открытым классовым интерфейсом.
Зашита закрытых данных класса опирается на защиту имен членов класса. Такую защиту можно обойти с помощью манипуляции адресами и явного преобразования типов (то есть обманным путем). Защита С++ есть защита от непреднамеренных ошибок, а не от умышленного нарушения правил. Только аппаратная зашита могла бы поставить надежный барьер на пути злонамеренных фокусов с универ- Метка риЫЫ делит тело класса на две части.
Имена в первой части (закрытой— рг(яаге) могут использоваться лишь функциями-членами. Вторая же часть — открытая (риЫ(с) и она формирует открытый интерфейс к объектам класса. Структуры есть по сути те же классы, но с открытым режимом доступа по умолчанию (810.2.8); функции-члены здесь определяются и используются одинаково. Например: 2В7 10,2. Классы сальным языком высокого уровня, что, однако, трудновыполнимо в реальных системах. Из-за закрытия данных в классе Ваге мы вынуждены были ввести функцию 1п11(), с помощью которой можно установить конкретное значение объекта класса.