Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 60
Текст из файла (страница 60)
К счастью, в европейском календаре нет нулевого года; первый год от 1'ождества Христова (деаг== 1) идет сразу же за первым годом до Рождества Христова (уеаг-= — 1). 10.2.4. Статические члены Удобство использования в Раге значения но умолчанию получено за счет серьезной скрытой проблемы.
Наш класс Оа1е теперь зависит от глобальной переменной 1ойау. Класс Ра1е можно теперь применять только в том контексте, в котором 1ойад определена и корректно попользуется во всем коде. Этот вид ограничений делает класс бесполезным вне контекста, в котором он был изначально написан. Пользователи получают слишком много неприятных сюрпризов, пытаясь воспользоваться подобными контекстно-зависнмымп классамп, и сопровождение становится чрезмерно сложным. Возможно, «всего лишь одна маленькая глобальная переменная' не станет слишком неуправляемой, но подобный стиль приводит к коду, которьш бесполезен для всех, кроме программиста, написавшего его, Этого с.ледует избегать.
К счастью, мы можем сохранить удобство значения по умолчанию и избежать неприятностей нз-за появления глобальной переменной. Переменная, которая является частью класса, но не является частью объекта этого класса, называется статическил! членом. Существует ровно одна копия статического члена, в отличии от обычных членов, когда каждьш объект класса имеет своп независимые члены, Аналогично, функнпя, которой требуется доступ к членам класса, но не требуется, чтобы она вызывалась для конкретного объекта класса, называется гпштической функнией-членом. Приведем пример, сохраняющий смысл конструктора значений по умолчанию Ра1е, в котором отсутствует проблема глобальной переменной: е1аее Ра!е ( !п1й т,у е1айс Ра1е йе)аи!! йа!е, риЫ!с Ра1е (т1йй=у, !и! тт=У, !п1уу=у) ууе!айс оогй ее1 йе7аи!! (!и1, гп1, тй Теперь мы можем онрелелпть конструктор Ра1е следующим образом: Ра1е; Рп1е (!п1 йй, !п1 тт, !п1уу( 275 10.2 Классы Й = ЙЙЗ сЫ: Йе)а1и1 Йа1е.Й, т= ттатт Йеза)1 Йа1е.т; у = уу Зуу Йе~аи11 Йа1е.у, О проверка на допустимость значения Ра1е Мы можем изменить дату по умолчанию в любой подходящий момент.
К статпческпгн членам можно обращаться так же, как и к любым другим членам. Кроме гого, к статическому члену молсно обращаться без указания имени объекта. Вместо етого в качестве квалифпкатора его имени используется нмя самого класса. Например: ооЫЯ ( Ра1е-ке1 Йе)аи)1 (4, 5, 1945), ) Статические члены — и функции и данные — должны быть где-то определены. На- пример: Ра1е Ра1еьйе1аи11 Йа1е (15, 12, 1770), иоЫРасеьке1 Йе)аи!1()л1Й, Ыст, !лгу) ( Ра1ечйезаи11 Йасе =Ра1е (Й, т, у); Теперь значением по умолчанию является дата рождения Бетховена (до тех пор, пока кто-нибудь не решит по-другому).
Обратите внимание, что Ра1е() служит другим обозначением для значения Ра1есЙе)аи)1 Йа1е. Например: Расе сору о1 Йеуаи11 Йа1е =Ра1е (); Следовательно, нам не нужна отдельная функция для чтения значения даты по умолчанию. 10.2.5. Копирование объектов класса По умолчанию, объекты класса можно копировать. В частности, объект некоторого класса можно проинициализнровать при помощи копирования объекта того жс класса. Это можно сделать даже там, где объявлен конструктор. Например: 11 инилиапизачиякопированием Расе Й = содау, По умолчанию, копия объекта класса содержит копию каждого члена.
Бели это не совсем то, что вам требуется для классаХ, можно реализовать более подходящее поведение, определив копирующий конструктор ХсХ(солк1Хс ). Это обсуждается в Ч 10,4А.1. Аналогично, объекты класса могут по умолчанию копироваться при помощи операции присваивания. Например: оо!Й1 (Расез, Й) Й.— — 1одау; Глава 10. Классы 276 И снова семантикой по умолчанию здесь является копирование каждого члена. Если это не годится для класса Х, пользователь может определить подходящий оператор присваивания Ц 10АА.1). 10.2.6. Константные функции-члены Определенный нами класс Ра1е предоставляет функции-члены, которые присваива- ют и изменяют значение объекта типа Ра1е.
К сожалению, мы не обеспечили способа проверки значения объекта Ра1е. Эту проблему можно легко решить, добавив функ- ции, возвращающие значения дня, месяца н года: с!аев Ва1е ( !п1с(, т, у; раб!!с гаг с!ау ,') сопз1 ( гегигп с1, ) тг топ!1~ () сопв1 ( ге1игп т, ) т1уеаг () сопвб 0- Обратите внимание на сопя! после (пустого) списка аргументов в объявлениях функций. Это означает, что зтц функции не изменяют состояние Ра1е. Естественно, компилятор обнаружит случайные попытки нарушить это обещание. Например: !п!тет10аРе уеаг() сопя! ( ге1игп у++; // ошибки: пони~тки налепить 71' вникание клена в констонтной функции Когда константная функция-член определяется вне класса, требуется суффикс сопз1: О правильно !п!!пе !и! Ва1е.
уеаг () сопз1 ( ге1игп у; Другими словами, суффикс сопИ является частью типа функпий Ра1есс(ау() и Ва1есуеаг (). Константную функцию-член можно вызвать как для константного, так и для не- константного объекта, в то время как неконстантную фущсцию-член можно вызвать только для объекта, не являюп1нгося константой, Например: 0 правильно О правильно сп1! = сс! уеаг () сс!.ас!с! уеаг (!); ооЫЯ(Ва1ес с1, сопз1 Ва1е8 сс)) ( !п1с = с(.уеаг(); с!.асЫ уеаг (!); О правильно О оитбка; нельзя изленить зничение константа сс! 10.2.
Классы 10.2.7. Ссылка на себя Функции-модификаторы состояния айй уеаг(), айй топЯ () и айй йау () были определены, как нс возвращающие значения. При использовашш подобных связанных функций иногда возникает хселание выстроить операции в цепочку. Для этого требуется, чтобы функции возвращали ссылку на измененный объект.
Например, мы могли бы написать эо!й/(Ра!е2 й) ( //- с!.айй йау(1) айй то«1Ь (1) айй уеаг(11 0 ... ) чтобы добавить один день, один месяц и один год к й. Для этого нужно, чтобы функ- ция возвращала ссылку на Ра!е: е1аев Ра1е ( //... 0 и! «бовпть и лет // прибавить и месяцев // ирис«вить п дней Ра!ед ас!й уеаг(сиги), Ра1езяайй топ!/с (т1п); Ра!ей айс1 йау(т!«), Каждая (нестатичсская) функция-член знает, для какого объекта она вызвана, н мо- жет явно на него ссылаться. Например: Расеи Р«1е ас!й уеаг(т!п) ( !/ (й= — 29 М т==2 ьпя йеаруеаг (у+п)) // не забудьте о 29 февраля // 1еаруеог — високосный год с! =1; си = 3; уя=п, ге!игп '11с!з, Р«1ейРа!есайй уеаг(т! «1 Выражение *1й!в означает объект, для которого вызвана функция-член.
Это эквивалентно ТНГ$ в сйпш1а и веЦ'в $ша))са)1с. В нестатпческой функции-члене ключевое слово !и!в является указателем на объект, для которого вызвана функция. В нестатической функции-члене классаХ Ягв имеет тип Х '. Однако, это не обычная переменная; невозможно получен ь ее адрес или присвоить ей что-нибудь. В константной функции-члене класса Х 1б!в имеет тип сопя! Х *для предотвращения модификации самого объекта (см.
также 9 бА.1 ). В большинстве случаев использование 1п!в является неявным. В частности, каждое обращение к нестатическому члену внутри класса неявно использует 1й!в для доступа к члену соответствующего объекта. Например, функцию айй уеаг можно определить эквивалентным, хотя и более пространным, способом: Глава 10. Классы 278 ,6' не зпбудыпе о 29 февраля 1(' (1Ыз — >6==29 ЬЬ ГЬЫ вЂ” >т=2 ВА ~(еаРУеаг (1ЬЫ->У+«((1 (Ыз->д =- 1, (Ыг — >т =Я, 1(г(з — >у - = п, гейггп '1Ьий Примером широко распространенного явного использования ЬЬгз являются опера- ции со связным списком (см., пап римс р, 9 24.3.7.4). 10.2.7.1. Физическое и логическое постоянство с(азз Ра(е ( Ьао1сасЬе иа(Ы, з( (пу сасЬе, иои1 сотри!е сасЬе иа(ие ((сапг1, ,(! -. рибйс (,(-. з(гильз(г!пу гер (! согт1, Д,( строковое предопавление Стачки зрения пользователя, з(г(пу гер не меняет состояния Ра1е, поэтому она, очевид- но, должна быть константной функцией-членом, С дру~ой стороны, требуется заполнить кэш перед его использованием.
Этого можно добиться, используя грубую силу: г1г(пуРа(е з(г(пу гер () сопз1 ср (сас(ге иа(И==За(зеН Ра1е* ГЬ = сопл( сазИЭа(е* > Яи) 1Ь->сотрще сасье иа(ие 3, ГЬ вЂ” >сасЬе иа1Ы = 1гие, О снимаем сопи приведением типа ге1ига сасЬе, Оператор сопя( саз( Я 15.4.2.1) используется для получения указа~ела типа Ра1е* на (Ь(в. Это вряд лп является элегантным решением и, кроме того, нет гарантии, что оно будет работать с обьектом, который был объявлен константой. Например: Иногда, функция-член с логической точки зрения является константной, но тем не менее ей требуется модифицировать некоторые члены.
С точки зрения пользователя функппя не изменяет состояние объекта. Однако незаметно для пользователя могут мспят~ ся некоторые части объекта. Это явление часто называют логи тгкиаг постояншпвом, Например, класс Ра1е может иметь функцию, возвращающую строковос представление, которое могло бы понадобиться пользователю для вывода.
Создание такого представления, в общем случае, может оказаться достаточно дорогой операцией. Поэтому, имело бы смысл хранить последнюю копию, чтобы при повторных запросах просто возвращать ее, если значение Ра(е не изменилось. Кэширование значений подобным образом чап(е встречается для более сложных структур данных, но давайте посмотрим, как это можно реализовать в Ра1е: 279 10.2. Классы 2!а!е д1; сопл! !1а!е г12; и!стив!= 61.в!г(пи гер ((; в1г1пив2 = д2 в!ппи гер ((, УУ неопределенное поведение В случае с а1 в!г(пд гер (( просто осуществляет обратное приведение к исходному типу Нн вызов будет работать.
Однако с(2 объявлена константой и реализация может использовать некоторую форму за!циты памяти д.чя гарантии того, что такой объект не будет разрушен. Следовательно, не гарантируется, что с(2.в!ппи гер (( выдаст одинаковый и предсказуемый результат во всех реализациях. 10.2.7.2. Объявление гпн1аЫе Можно избежать явного преобразования тина «снятие сопз! путем приведения> и последующей зависимости от реализации, объявив данные, участвующие в управлении кзщ-памятью, как ти1аЫе: г1авв !ла!е ( гпи!аЫе Ьоо(сас1!е иа1!д; тигаЫе в!г!пи сасЬе; ио!г! сотри1е саиде ип(ие ((; О.— риЫ!с; У,1" в!г!пи и!г!пи гер (( еопв1; !'!' сп!роковое предгтивление Квалификатор хранения !пи1аЫе указывает, что член должен храниться таким способом, чтобы допускалась его модификация, даже если он является членом константного объекта.