Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 60
Текст из файла (страница 60)
10.2З. Конструкторы Использование функций типа 1п11() для инициализации объектов класса и не- элегантно и чревато ошибками. Действительно, поскольку нигде прямо не указано, что объект класса нужно инициализировать таким образом, программист может забыть об этом или, наоборот, дважды вызвать функцию 1п11() (часто с одинаковыми разрушительными последствиями). В итоге, лучше дать возможность программисту объявлять специальные функции-члены, явно предназначенные именно для инициализации объектов. Так как такие функции участвуют в создании (конструировании) объектов, их принято называть конструкторами (сопгггисгогл), конструкторы выделяются тем, что их имена совпадают с именем класса. Например: с1аев Ваге ( У...
Ваге()пг, 1пб ии); )' // ко нструкпюр Если класс имеет конструкторы, то любой объект класса инициализируется вызовом конструктора (510.4.3), причем если конструктор имеет аргументы, то ему их требуется передать: Ваге «гг(ау = Ваге (23, 6, 1983); Ваге хгпав (25, 12, 1990); Васе ту Ь(ггйаау; Расе ге1еаве1 0 (10, 12); // сокращенная форма // еггог.' отсутствует инициализация У еггог.' отсутствует третий аргумент Часто бывает полезным иметь несколько способов инициализации классовых объектов.
Этого можно достичь, определив в классе несколько конструкторов. Например: с1аев Ваге ( 1пгд, т, уг рил11: У ... Ваге(гпб агб 1пг] г Ваге (1пг, гпг); Ваге (1пе) Г Расе() Г Ваге(сонм айаг*) г // день, месяц, од /У день, месяц, текущий год //день, текущие месяц и год // дата по умолчанию: сегодня У дата в строковом представлении Конструкторы подчиняются тем же самым правилам перегрузки, что и обычные функции (57.4).
До тех пор, пока конструкторы существенно отличаются друг от друга набором аргументов, у компилятора есть возможность выбрать правильный вариант: Глава 10. Классы 288 Ваге Годау (4); Ваге /и!у4 ( чуи1у 4, 1983" ); Ваге виу(»5 зуои'); Ваге поич //умолчотел»ная инициализация текущей датой Чрезмерное увеличение числа конструкторов, как в показанном примере класса Ваге, является типичным явлением. В процессе проектирования класса у програм-, миста возникает искушение на всякий случай добавить любые средства просто потому, что они могут кому-нибудь понадобиться. Большие умственные усилия требуются, чтобы четко ограничить набор средств действительно необходимым минимумом.
Эти дополнительные интеллектуальные усилия приводят в типичном случае к более ясным и более компактным программам. Одним из способов уменьшения количества необходимых функций является применение «аргументов по умолчанию» (З7.5). Для класса Ваге можно предусмотреть умолчательные значения аргументов, трактуемые как «сегодня» (годау): с1ат Ваге ( 1пгд, т,у; риЫзс: Ваге(?пг сЫ=О, 1пе тт =О, Ызуу =О); //... Ва(е::Ва(е((пг 44, тг тт, теуу) ( И = 44?дймодау.дз т = тт? тт: годау. т; у = уу?уу: гойау.уз /' проверка допустимости даты ) Для умолчательных значений аргументов нужно предусмотреть, чтобы они не совпадали с возможными «обычными» значениями аргументов, Для дней и месяцев зто достигнуто с очевидностью (не существует нулевых дней и месяцев), но для года это также имеет место (хотя и не так очевидно), ибо в европейском календаре нет нулевого года; первый год от Рождества Христова следует сразу же за первым годом до Рождества Христова (минус первый год).
10.2.4. Статические члены Удобство умолчательных аргументов в классе Ваге достигнуто за счет следующей скрытой проблемы. Класс Ваге теперь зависит от глобальной переменной годау. В результате безошибочное использование класса Ваге возможно лишь в контексте определения и корректного использования глобальной переменной годау всеми частями программы. Такого рода ограничение делает класс бесполезным вне контекста, в котором он был изначально написан. Пользователи получают множество неприятных сюрпризов, когда они пытаются воспользоваться контекстно-зависимыми классами, а сопровождение программ становится слишком сложным.
Может, «одна небольшая глобальная переменная» и не повредит, но вообще такой стиль программирования приводит к коду, 289 18.2 Классы бесполезному для всех, кроме написавшего его программиста. Этого следует избегать. К счастью, имеется возможность и удобства умолчательных значений сохранить, и глобальных переменных избежать. Переменная, являющаяся членом класса, но не частью объекта класса, называется статическим членом (з(а11с тетбег). Она всегда существует в единственном экземпляре, в отличие от обычных членов класса, дублирукяцихся во всех классовых объектах (9С.9).
Аналогично, функция, которой нужен доступ к членам класса, но не требуется ее вызов для конкретных объектов класса, называется статической функцией-членом. Вот переработанный вариант класса Расе, сохраняющий семантИку умолчательного конструирования, но не опирающийся при этом на глобальные переменные: с1ат Риге ( (пей, т,у; лза11с Ра1е йе~аи11 йа(е; риЫ(с: Расе(1пг ЙЙ=О, (пс тт=О, 1пс уу =0) 77 ... ззайс ю14 лез Йе5аи11 (1пб тз, тг); )' Теперь мы можем определить конструктор класса Риге так, чтобы использовать Йегаи11 йаге: Ра(е::Разе (1п( ЙЙ„1пз тт, 1пг уу) ( й = Йй? Йй: Йеуаи(1 Йазе.й; т = тт? тт: Йе~аи1( йа1е.т; У =УУ? УУ: йезсаи1( йа(е.уз 77 проверка допустимости даты ) Используя лег Йеуаи11(), мы в любое время можем изменить умолчательное значение даты.
К статическому члену можно обращаться так же, как и к любому другому. Дополнительно, к нему можно обращаться без указания классового объекта. Вместо этого следует квалифицировать его имя именем класса. Например: ю145'() ( Разе:: зет Йе5аи11 (4, 5, 19445) ) 77 обращение к статической функции 77 зе( Йе~аи(10 класса Ра(е Статические члены — как данные, так и методы — должны быть определены. Ключевое слово агапе при этом повторять не надо. Вот пример: Уопределнем Ра(екйеуаи1( йа(е Разе Разе:: Йеуаи(1 Йазе (1б, 11, 1770); юЫРазе::лес йегаи11(1пг Й, 1пз т, 1пгу) Уопределнем Ра(екзе( Йе)аи11() ( Йе(аи(1 Йаге = Риге (й, т,у); 77присваиваем новое значение умолчательной дате Глава ) О. Классы 200 Теперь умолчательная дата совпадает с днем рождения Бетховена (пока кто-то не задаст иное умолчательное значение). Заметим, что выражение Ваге() служит иным обозначением умолчательного значения Ваге:: ае~аий ваге.
Например: Риге сору о/ ае)аи)1 йе1е = Роге () 1 Следовательно, нам не нужна отдельная функция для чтения умолчательной даты. 10.2.5. Копирование объектов класса Возможность копирования классовых объектов доступна изначально. В частности, объект класса можно инициализировать копией другого объекта того же класса; это можно делать и в случае, когда определены конструкторы. Например: ~У инициализация копированием Ра1е й = 1ойау; По умолчанию, копия объекта класса содержит копии всех полей объекта. Если это не то, что нужно, следует явно определить копирующий конструктор (сору соплгис(ог) вида Х:: Х(сопят Ха) (подробно обсуждается далее в 0104 4.1).
Аналогично, классовые объекты можно копировать с помощью операции присваивания (озз1яптепг орегогог). Например: гогйу'(Рагеа й) ( а' = 1ойауг ) И снова умолчательной семантикой о))ерации является почленное копирование. Если это не подходит для некоторого класса Х, то можно определить специфическую версию операции присваивания именно для этого класса (э10.4.4.1). 10.2.6.
Константные функции-члены В определенном нами классе Ваге имеются функции-члены, позволяющие задавать и изменять значения объектов этого класса. К сожалению, пока что отсутствует способ узнать эти значения. Проблема легко устраняется введением функций для чтения дня, месяца и года: е1аее Роге ( унга', т, у) риЫ)е: тг йау () сопи (ге1игп 41 ) !пг топ)я () еопе( (ге1игп тг ) )пгуеаг(] сопзг; ~У ...
)1 Обратите внимание на ключевое слово сопзг после круглых скобок в объявлениях (определениях) функций. Оно означает, что эти функции не изменяют состояния обьектов класса Ваге (доступ по чтению). Естественно, что компиляторы обнаруживают случайные попытки нарушить это обещание. Например: 10.2. Классы ш!!пе !лг Ра1е::уеаг() сопл ( гетгп уъ+1 //еггогь попытка изменить состояние объекта в константной функции ) Когда константная Функция-член определяется вне тела определения класса, требуется повторять ключевое слово сопле.
ш!!пе шг Ра1е::уеаг() сопл! Уток правильно ( ге1игп уг ) гпяле !лг Разе::уеаг () ( ге(игл у( Другими словами, суффикс соазт является неотъемлемой частью типа Ра1е::йау(), Разе::топй() и Роте::уеаг(). Константные функции-члены могут вызываться для солт!- и не солт!-объектов классов, в то время как неконстантные функции-члены — лишь для не вольт-объектов.
Например: го!йу (Рагеь й, сопл! Рагеа сй) ( !пг ! = й.уеаг () 1 // о(г й,айй уеаг(1) 1 // о(г па1' = сй.уеаг() ! // ок сй.айй уеаг(1) 1 // еггог: нельзя изменять значение константы сй 10.2.7. Ссылки на себя Функции айй уеаг(), вЫ топтй() и айй йау(), призванные изменять значения объектов типа Ваге, не имели возврата (указан как гоЫ).