Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 218
Текст из файла (страница 218)
Например: оо!с!1(овс1ог<з1г!пу>й о, сопз1!оса!ей ту !оса1е) зогг(о.Ьеу!и(), о.епс!()); // сорпшровко с использовиниел < для сравнения элементов //-. зогг(о.беу!и(), оспа!(), ту !оса!е), // сортировка в соответтпвии // с правилали ту !осо!в При сравнении функция зог/() стандартной библиотеки по умолчанию использует оператор < с численными значениями набора символов конкретной реализации Я 18.7, ~ 18.6.3.1). Обратите внимание, что локализации сравнивают объекты типа Ьаз!с з1г!лу, а не С-строки.
Г.З. Фасеты локализаций Фасет (Касе1) являстся объектом класса, производного от класса-члена /асе1 класса !оса!е: с!азз зйИоса!е::1асе1 ( рго1есгес!: скр!!с!1 /асяс(з!зе 1 г = 18; // 'г= =0': локализация управляет зрел~внял // жизни срасвта Приложение Г Локализация 960 о!с!ив!-/асе!() рг!оа!ег /асе!(сапе!/асеЯ; оо!с! врега1аг=(сапе!/асе!с), // внимание: заи!!ииенныйдеструктор // не определен // не определен // представление класса оо!с1,/(сапе! з!г!пдй з1, сопи! и!г!пдй з2) // обычный случай: !по умолчанию) аргумент 0 означает, что за уничтожение // отвечает локализация: свИаге<сваг * р = пет свИа!е Ьупате<сваг>("рр); 1оса1е 1ос(1оса1е(), р); // редкий случай: аргумент ! означиет, чта за уничтожение отвечает // пользователю саИа!е<сааг>* у = песс соИаее Ьупате<сваг>("уе", 1), соИаге Ьупате<сваг> Ьиу!("зш"); соИа1е Ьупате<сваг Ьиу2('по, 1); // ошибка: нельзя уничпьожить // л окальную переложенную // ошибка; нельзя уничтожить //локальную переменную // д нельзя удалить: деструктор со!!а1е Ьупате <сдпг> заи!ии!ен // и нельзя удалшпь р; за удаление 'р выл вечает лакал азалия Таким образом, стандартные фасеты полезны, когда управляются локализациями (как базовые классы), и очень редко — в других случаях.
Фасст Ьдпате() является фасетом из именованной локализации среды исполнения (9 Г.2 1). Для того, чтобы поиск фасета в локализации можно было осуществлять при помощи Ьаз/асе1() и изе ~псе!() (9 Г.3.1), для каждого фасета должен быть определен идентификатор Ы: Операции копирования объявлены закрытыми и оставлены неопределенными для предотвращения копирования (9 11.2.2). Класс Гасе! разработан в качестве базового, в нем нет открытых функций. Конструктор этого класса объявлен защищенным для предотвращения создания объектов типа «просто /асе!», а деструктор является виртуальным для обеспечения корректного уничтожения обьектов производных классов. Предполагается, что объекты 1оса1е будут обращаться с /асе1 при помощи указателей. Лргумент 0 конструктора /асе1 означает, что 1оса1е обязан удалить/асе1 после исчезновения последней ссылки на него.
Напротив, ненулевой аргумент конструктора гарантирует, что 1оса1е никогда не удалит Гасе!. Непуленое значение аргументз задумано на тот редкий случай, когда время жизни фасета управляется не косвенно локализацией, а непосредственно программистом. Например, мы можем попытаться создать обьекты стандартного типа фасета соИа1е булате<сЬаг> (9 ГА.1.1) следующим образом: 961 Г.З. Фасеты локализаций с1азз зЫс!оса1е..Ы ( рид!!с. Ы((; рг!оа1е: Ы(сопя!!дй(; // не определен ио!И орегагог=(сонм !дй(; // не определен // представление ); Операции копирования объявлены закрытыми и оставлены неопределенными для предотвращения копирования (9 11.2.2). Класс Ы задуман для того, чтобы пользователь мог определить статический член типа Ы в каждом классе, предоставляющем новый интерфейс фасета (например, см. 9 Г.4.1).
Механизмы локализации используют объекты Ы для идентификации фасетов (9 Г.2, 9 Г.З.! ). Б очевидной реализации локализации, Ы применяется как индекс вектора указателей на фасетьк реализуя таким образом аффективный тар<Ы, /асе/*>. Данные, используемые для определения (производных) фасетов, определяются в производном классе, а не в самом базовом классе /асе1. Это подразумевает, что программист, определяющий фасет, имеет полный контроль над данными, и что для реализации представляемого фасетом понятия может быть использован произвольный объем данных. Обратите внимание, что все функции-члены определенного пользователем фасета должны быть сопя!-членами. Как правило, фасет предполагается неизменяемым Я Г.2.2). Г.3.1.
Доступ к фасетам локализации Доступ к фасетам локализации осуществляется при помощи шаблонной функции ике ~асе1. Мы может выяснить, содержит ли локализация данный конкретный фасет, при помощи шаблонной функции /как /асе1; 1етр!а1е <с!аяз Ракет Ьоо! /кая /асе1(сопя1 !оса!ей( 1/чгоа~~; 1етр!а1е<с!азя гасеР сопз1 Расегй изе /асег(сопя! !оса!ей(; //к~ожет сгенерировать //6ад сая1 Можно считать, что зти шаблонные функции осуществляют поиск шаблонного параметра Расе1 в аргументе !оса!е.
Или посмотрим на ике ~асе! как на своего рода явное преобразование (приведение) типа локализации в тип конкретного фасета. Такая возможность обеспечена тем, что у локшчизации может быть только один фасет данного типа, Например: во!дЯсопз1 !оса!ей ту !оса!е( ( с/чаг с = ике /асе1< питрипс1<с/каг» (ту !оса!е(л1ес!та1 ро!и1((; О использование стандартного фасета Я/чак /асе1<Епсгург>(ту !оса!е(( ( // в ту !оса!е илгеетсл фасет Епсгур1? сопя1 Епсгуряй/' = изе /асе1<Епсгургк(ту !оса!е)! // извлечение фисета ,Д Епсгур1 9Б2 Приложение Г Локализация сопя! Сгур1о с=Яае1 сгур1о() // использование фасета Епсгур1 //-.
Обратите внимание, что ияе /асс! возвращает ссылку на константный фасет, поэтому мы не можем присвоить результат изе /асе1 не-константе. Это разумно, потому что подразумевается, что фасет неизменяем и содержит только константные члены. Если мы напишем ияе /асе1<Х>(!ос), а фасета Х в !ос нет, ияе )асе1(( сгенерирует исключение Ьас! сия!Я !4ЛО). Гарантируется, что стандартные фасеты доступны во всех локализациях !3 ГА), так что для стандартного фасета нет необходимости обращаться к Ьая Гасе!.
Со стандартнымн фасетами азе /асе1 не сгенерирует Ьас! сая1. Как можно реализовать изе ~асе1 и Ьая/асе1? Вспомните, что мы вправе рассматривать 1оса!е как тар<Ы, /асе1*> Я Г.2). Располагая типом ~асе1 в качестве шаблона аргумента Гасе!, реализация Ьаз /асе1 или изе /асе1 в состоянии обратиться к Еасе1хЫ и использовать его для поиска соответствующего фасета. Весьма наивная реализация функций Ьая ~асе1 и ияе ~псе! могла бы выглядеть следующим образом: // псевдореалияв ция: предстивияь ~то локалияаиия содержит ,',' ассоииип|ивпий массив тар<сб/асе1*' с ипепел!исе1 псар 1етр!а1е <с!азз расея> Ьоо! Ьая /асе1(сопя1 !оса!ей !ос( 16гоив(( соси!!оса!е:/асег< Г= !ос ~асе1 тир(расесдс!(; ге1игп/З 1гие;/а!яе; 1етр!а1е <с!азя гасе1> сопя1 расе1й ияе /всея!ропз1 !оса!ей !ос( ( сопя! !оса!е"~асеР~= !ос~асе1 тир(расе1 дК~, !/'(/( ге1игп з1анс сая1<сопя1 Еасегй>(*/(, 1Ьгот бас! сая1(), По-другому механизм/асе1хЫ можно интерпретировать как реализацию полнморфизма этапа компиляции.
Очень близкий каяе ~асе1 результат получается применением с!упат!с сая1. Однако специализированная изе /асе1 реализуется куда эффективнее более общего механизма с!упат!с саз1. Б действительности, Ы идентифицирует скорее интерфейс и поведение, чем класс. То есть если интерфейсы двух классов фасетов в точности совпадают, и оба класса реализуют одну и ту же семантику !насколько это имеет отношение к локализации), они должны идентифицироваться одним и тем же Ы. Например, со!!а1е<сйаг> н со!!а1е Ьупате<сйаг взаимозаменяемы в.локализации, поэтому они оба идентифицируются со!!а1е<сйаг>хЫ Я Г.4.1). При определении фасста с новым интерфейсом — вроде Епсгур1 в /(' — мы должны определить и соответствующий Ы для идентификации фасета (см.
~ Г.З.2 и 5 Г.4П), Г.З. Фасеты локализаций 963 Г.3.2. Простой определяемый пользователем фасет Стандартная библиотека предоставляет стандартные фасеты, учитывающие наиболее супиественные области культурных различий, такие как набор символов и ввод/ вывод чисел. Чтобы познакомиться с механизмом фасетов отдельно от всякого рода сложностей, связанных с широко используемыми типами и сообраиксниями производительности, позвольте мне сначала описать фасет для тривиального определяемого пользователем типа; ели т Беакоп ( зридпО, киттег, (а!1, в(пзег); // весна, лето, осень, зсаса Это простейший пользовательский тип, какой я только сумел выдумать. Очерченный ниже стиль написания ввода/вывода может использоваться (с минимальнымн вариациями) для большинства простых определяемых пользователем типов: с(аик Беазоп (о, ри61гс!оса!е::Гасе! ( рибйс: Беазоп 1о(1п11= О( !оса(е../асе1(1(() -Беаиоп 1о((() Д чтобы обеспечить возяожносспь уничтожения // объектов типо Беизоп го (у' 1:3) и(гзиа(сопк1 и1ьдпяй 1о згг(Бепиани( сопи!= О; // строковое представление и // полистать Беакоп, соответстврюи1ий и, в к: Ыг1иа! Ьоо!/гот згг(сопи! игг(пай к, Беаиопй х( сопи! = О; игалс1оса(ез!ЙЫ; //объектидентификатор4асета 13 Д2, 3 1 3, 3 73 !) ); !оса(есЫБеазоп (о:Ы; //определение объекта-идентп4икатора Для простоты данный фасет ограничивается представлениями, использующими сдпг.
Класс Беазоп Ы обеспечивает общий абстрактньш интерфейс для всех своих фасетов. Для определения представления ввода/вывода Яеазоп в конкретной локализации, мы наследуем класс от Беаиол Ы, определив при атом подходящие 1о з1г() и/гот и1г((. Вывести Беакоп просто. Гслн у потока имеется фасет Беакоп 1о, мы можем воспользоваться последним для преобразования времени года в строку.
Если нет, выведем целое значение Беакол: ои1геатй орега1ог«(оиггеатй к, Беазоп х( ( сопк1 (оса!ей (ос = кденос((; // извлечем локализацию патока ('3" 21.7.1) 1/(6аи !асе1<Беаиоп Ы>(1ос(( гевзгпк «изе /асе1<Беаиоп 1о>(1ос(го игг(х(; ге1игп к «(а1(х(; ) Обратите внимание, что реализация оператора «применяет его же, но с другими типами. Благодаря атому мы можем воспользоваться преимуществами простоты вызова «по сравнению с прямым доступом к буферам оз1 еат; преимуществами чувствительности «к локализациям и, наконец, прсимуществами обработки ошибок, обеспечиваемой для «.
Стандартные фасеты склонны непосредственно обращаться к буферам потока Я ГА.2.2, ~ ГА,2,3) для достижения максимальной эффективности н гибкости, но для многих простых определяемых пользователем типов нет необходимости опускаться на уровень абстракции з1геатбиГ. Приложение Г Локализация Как обычно, ввод немного сложнее вывода: /я!геатй орега!о! >(гв!геатй ч, Беаяопйх) ( сопя! !оса!ей !ос= яхье!1ос(); // извлечем локализшрио потока Ц 21.?.1) аЯЬая /асе!сБеаяоп !о>(1ос)) ( // чтение символьного представления сопя1 Беаяоп !ой/= иве /асе!сБеаяоп !о>(1ос); я!г!пя ЬиД; !Я)(я»Ьи? ййПгот я!г(Ьи?, х))) я.яе!я!а!е(!оя Ьаяе:Уа!1Ь1!)- ге!игл я; // чтение числового представления !и!б я » с х = Беаяоп(ь); ге!игп я, Обработка ошибок проста и следует стилю обработки ошибок встроенных типов.