Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 98
Текст из файла (страница 98)
Язык С() // Следующая строка, по существу, неоднозначна! // Неясно, делается ли в ней ссылка на класс // Соипсэонп из пространства имен СонпГЕГ илИ // АпокйегСоппсег? Соспсэоип сб1 = пен Соппкоонп(10)т // Ошибка! ! ! Если попытаться скомпилировать эту программу, то будет получено сообщение об ошибке, уведомляющее о неоднозначности в следующей строке кода из метода Мауп (): Соопкоонп сб1 = пен СочпГОонп(10)! // Ошибка! ! ! Причина подобной неоднозначности заключается в том, что в обоих пространствах имен, Соопсег и Апос)тегСоппсег, объявлен класс Соопгпоип и оба пространства сделаны видимыми.
Поэтому неясно, к какому именно варианту класса соипгпоип следует отнести приведенное выше объявление. Для устранения подобного рода недоразумений и предназначен описатель::. Для того чтобы воспользоваться описателем::, необходимо сначала определить псевдоним для пространства имен, которое требуется описать, а затем дополнить описание неоднозначного элемента этим псевдонимом. Ниже приведен вариант предыдущего примера программы, в котором устраняется упомянутая выше неоднозначность. // Продемонстрировать применение олисатеЛя::. чя1пч Буясеш! чзапд Соспсег! сяапч Апосьегсоипсег! // присвоить классу сочпгег псевдоним сгг. чяапч Ссг = Сонпкею // Объявить пространство имен для счетчиков. пашеярасе Сонпгег ( // Простой вычитающий счетчик, с1аяя Сочпсоонп ( апг ча1) рн)т11с Соипсвонп(апс и) ( ча1 = пт ) // Объявить еще одно пространство имен для счетчиков.
пашеярасе Апогйегсонпгег ( // Объявить еще один класс Соипгэонп, принадлежащий // пространству имен Апосьегсоспгег. с1аяя Соппсоонп ( 1пс на1т Глава 16. пространства имен, лрвпроцвссор и сборки 509 рчп11с Соипсоонп(1пк п) ( ча1 = пт ) // ) с1азя А11аядча1111егпешо ( ясасгс чоки Иагп() ( // Здесь оператор:: разрешает конфликт, предписывая // компилятору использовать класс Соппгронп из // пространства имен Сочпсег. Ссг::Сочпсоонп сб1 = пен Ссг::Соппсоонп(10)т // ) ) В этом варианте программы для класса Соппгег сначала указывается псевдоним Сгг в следующей строке кода: оя1пд Ссг = Соппсег А затем в методе иафп () этот псевдоним используется для дополнительного описания класса соппсРонп, как показано ниже.
Ссг::Соппгоонп ск)1 = пен Ссг::Соппсвочп(10) Описатель:: устраняет неоднозначность, поскольку ои явно указывает на то, что следует обратиться к классу соппспонп из пространства ссг, а фактически — соипсег. Именно зто и делает теперь программу пригодной для компиляции. Описатель:: можно также использовать вместе с предопределенным идеитификатором д1ОЬа1 для ссылки иа глобальное пространство имен.
Например, в приведенной ниже программе класс Соппепонп объявляется как в пространстве имен Соппеег, так и в глобальном пространстве имен. А для доступа к варианту класса Соппкпонп в глобальном пространстве имен служит предопределенный псевдоним д1оЬа1. // Использовать псевдоним глобального пространства имен. оя1пд Буясешт // Присвоить классу Соппсег псевдоним Ссг.
иягпд Ссг = Сочпгег; // Объявить пространство имен для счетчиков. пашеярасе Соппгег ( // простой вычиташший счетчик. с1аяз Сонпсронп ( апг ча1т рпо11с Соппгоонп(1пг и) ( ча1 = пт ) 510 Часть 1. Язык С() // Объявить еше один класс Соппгоомп, принадлежаший // глобальному пространству имен. с1авз Соппсоонп ( Епг на1т рпв11с Соппгооип(ъпк и) ( на1 = ги с1авз 61оЬа1й11авбпа11ЕЕегоешо ( всакъс нота Иатп() ( // Здесь описатель:: предписывает компилятору // использовать класс Соппкоонп из пространства // имен Соппкег. Скгс:Соппсоонп са1 пен Ссг::Соппсоонп(10)) // далее создать объект класса Соппсоонп из // глобального пространства имен. 01оЬа1::Соппсоонп сб2 = пен 01оЬа1с:Соппсоонп(10) //'. ) ) Обратите внимание на то, что идентификатор д10Ьа1 служит для доступа к классу Сопптпоип из используемого по умолчанию пространства имен.
01оЬа1:сСоппсоонп сб2 пен 01оЬа1с:Соппсоонп(10)) Этот подход можно распространить на любую ситуацию, в которой требуется указывать используемое по умолчанию пространство имен. Препроцессор Збевтпе Зепбгечзоп Ургаоша зе11Е Феггог Фге01оп Зе1ве Йепа1Е Ф11пе Знагпзпо Зппбет В языке Си определен ряд директив препроцессора, оказывающих влияние на интерпретацию исходного кода программы компилятором.
Эти директивы определяют порядок интерпретации текста программы перед ее трансляцией в объектный код в том исходном файле, где они появляются. Термин директлива ирелроцессора появился в связи с тем, что подобные инструкции по традиции обрабатывались на отдельной стадии компиляции, называемой препроцессором. Обрабатывать директивы на отдельной стадии препроцессора в современных компиляторах уже не нужно, но само ее название закрепилось. Ниже приведены директивы препроцессора, определенные в С(). Глава 16.
Пространства имен, нрепроцессор и сборки 51 1 Все директивы препроцессора начинаются со знака №. Кроме того, каждая директива препроцессора должна быть выделена в отдельную строку кода. Принимая во внимание современную объектно-ориентированную архитектуру языка С№, потребность в директивах препроцессора в нем не столь велика, как в языках программирования предыдущих поколений. Тем не менее они могут быть иногда полезными, особенно для условной компиляции. В этом разделе все директивы препроцессора рассматриваются по очереди.
Директива $йейхае Директива №пег1пе определяет последовательность символов, называемую идентификатором. Присутствие или отсутствие идентификатора может быть определено с помощью директивы №11 или №е111 и поэтому используется для управления процессом компиляции. Ниже приведена общая форма директивы №пе11пе. №ае11пе идентификатор Обратите внимание на отсутствие точки с запятой в конце этого оператора.
Между директивой №пе11пе и идентификатором может быть любое количество пробелов, но после самого идентификатора должен следовать только символ новой строки. Так, для определения идентификатора ЕХРЕЕ1ИЕНТАЬ служит следующая директива: №ает1пе ЕХРЕКгнкитлъ Помни! В С/С.ь+ директива №Пебфпе может использоваться для подстановки исходного текста, например для определения имени значения, а также для создания макрокоманд, похожих на функции. А в С№ таков применение дирвкгпивы №се ге не нв поддерживается. В этом языке директива №ае11пе служит только для определения идентификатора.
Директивы $хй и $ее1с1хЕ Обе директивы, №хг и №епд11, допускают условную компиляцию последовательности кода в зависимости от истинного результата вычисления выражения, включающего в себя один или более идентификатор. Идентификатор считается истинным, если он определен, иначе — ложным. Так, если идентификатор определен директивой №Пебхпе, то он будет оценен как истинный. Ниже приведена общая форма директивы №11. №11 идвнтификаторное выражение последовательность операторов №епа11 Если идентификаторнсе выражение, следующее после директивы №Ег, истинно, то компилируется код (последовательность операторов), указываемый мелгду ним и директивой №епп11. В противном случае этот промежуточный код пропускается. Директива №епп11 обозначает конец блока директивы №11. Идентификаторное выражение может быть простым, как наименование идентификатора.
В то же время в нем разрешается применение следующих операторов: !, ==, ! =, ее и | К а также круглых скобок. Ниже приведен пример применения упомянутых выше директив. // Продемонстрировать применение директив // №11, №епаьт и №аетхпе. 512 часть ), язык Са «бе11пе БХРБАТМЕИтАЬ иэ1по Зуэгепы с1аэя Тенг ( эгагас тоаб Ма1п() ( «11 БхРеп1меитАь Сопэо1е.ИсагеЬ1пе("Компилируется для экспериментальной версии."); «епбаг Сопяо1е.ИхасеЬЬпе("Присутствует во всех версиях."); ) ) Этот код выдает следующий результат: Компилируется для экспериментальной версии.
присутствует во всех версиях. В приведенном выше коде определяется идентификатор ЕХРЕЕ1МЕИТАЬ. Поэтому когда в этом коде встречается директива фЬТ, идеитификаториое выражение вычисляется как истиииое и затем компилируется первый оператор, содержащий вызов метода И11сеЬ1пе () . Если же удалить определение идентификатора ЕХРЕЕ1МЕИТАЬ и перекомпилировать данный код, то первый оператор, содержащий вызов метода И11севфпе (), ие будет скомпилировав, поскольку идеитификаториое выражение директивы ф11 вычисляется как ложное.
Но второй оператор, содержащий вызов метода ИгагеЬЬпе (), компилируется в любом случае, потому что ои ие входит в блок директивы ффт. Как пояснялось выше, в директиве ф11 допускается указывать идеитификаториое выражение. В качестве примера рассмотрим следующую программу. // Испольэовать идентификаторное выражение.
«беттпе ЕХРЕК1МЕМТАЬ «бегтпе ТЯТАЬ пяапо Яуягеьы с1аээ Тенг ( ягатас то1б Ма1п() ( $11 ЕХРЕК1МЕМТА1 Сопэо1е.ихасеЬЬпе("Компилируется для экспериментальной версии."); «епб11 411 БХРЕКТМБМТАЬ ьь ТЕ1АЬ Сопяо1е.вхгох.иг1гевапе("Проверка пробной экспериментальной версии."); аепб11 Сопэо1е.игагеЬЬпе("Присутствует во всех версиях."); ) ) Эта программа дает следующий результат: Глава (6. Пространства имен, препроцессор и сборки 513 Компилируется для экспериментальной версии.
Проверка пробной экспериментальной версии. Присутствует во всех версиях. В данном примере определены два идентификатора: ехрее1мемтйь и тетйь, Второй оператор, содержащий вызов метода ИТЬСеЬЬпе (), компилируется лишь в том случае, если определены оба идентификатора. Для компилирования кода в том случае, если идентификатор не определен, можно воспользоваться оператором !, как в приведенном ниже примере. ВЬТ !ЕХРЕН1МЕМТАЬ Сопво1е.ИгасеЬЬпе("Этот код не экспериментальный!"); Еепдьг Вызов метода будет скомпилирован только в том случае, если идентификатор ЕХРЕЕ1МЕМТАЬ не определен.
Директивы $е1зе и Ме1хй Директива Фе1 ве действует аналогично условному оператору е1 э е языка СФ, определяя альтернативный ход выполнения программы, если этого не может сделать директива бЬТ. С учетом директивы Уе1ве предыдущий пример программы может быть расширен следующим образом: // продемонстрировать применение директивы Ве1ве.
Едетапе ЕХРЕНТМЕМтАЬ паьпд Яуасепи с1аэе тевк ( впакьс чоад Мати() ( ВЬТ ЕХРЕН1МЕМТАЬ Сопяо1е.ИгькеЬЬпе("Компилируется для экспериментальной версии."); Ее1ве Сопво1е.Игькевьпе( "компилируется для окончательной версии."); Вепд11 «11 ехРек1мемтАь еа те1А1 Сопао1е.Еккок.игткеЬЬпе("Проверка пробной экспериментальной версии.") Ве1яе Сопво1е.Еттох.игтсеЬЬпе( "Это не пробная экспериментальная версия."); Уепдтт Сопэо1е.нк1кеЬЬпе("Присутствует во всех версиях.") ) ) Вот к какому результату приводит выполнение этой программы: Компилируется для экспериментальной версии.