Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АрхитектураБиологияГеографияДругоеИностранные языки
ИнформатикаИсторияКультураЛитератураМатематика
МедицинаМеханикаОбразованиеОхрана трудаПедагогика
ПолитикаПравоПрограммированиеПсихологияРелигия
СоциологияСпортСтроительствоФизикаФилософия
ФинансыХимияЭкологияЭкономикаЭлектроника

Программирование в Visual С++ 2005 9 страница



 

 


Пространство имен myStuf f определяет область видимости, и все, что находится
внутри него, квалифицировано именем этого пространства имен. Чтобы сослаться
извне на имя, объявленное внутри пространства имен, вы должны квалифицировать
его именем пространства имен. Внутри же области видимости пространства имен на
любое имя, объявленное в нем, можно ссылаться без квалификации — здесь все иден-
тификаторы принадлежат одному семейству. Но в функции main () вы теперь должны
квалифицировать имя value префиксом myStuf f, иначе программа не скомпилиру-
ется. Функция main () теперь ссылается на имена из двух разных пространств, и в
общем случае вы можете иметь столько пространств имен в вашей программе, сколь-
ко вам нужно. Вы можете исключить необходимость квалификации имени value, до-
бавив директиву using:

 

 




Вы также можете вставить директиву using и для пространства имен std, так что
вам не придется квалифицировать имена из стандартной библиотеки, однако это от-
меняет саму идею пространств имен. Вообще говоря, если вы применяете простран-
ства имен в своей программе, то вы не должны добавлять директивы using по всей
программе, также вы можете не беспокоиться о них с самого начала. Но, отметив
это, мы все же будем добавлять директиву using для std во всех примерах, чтобы код
был менее громоздким и легче читался. Когда вы начинаете изучать новый язык про-
граммирования, то должны стараться обходиться без усложнения кода, независимо
от того, насколько это применяется на практике.

Множественные пространства имен

Реальные программы, с которыми вам придется столкнуться, скорее всего, будут
включать в себя множество пространств имен. Вы можете иметь множество объявле-
ний пространств с одним и тем же именем, при этом содержимое всех одноименных
блоков пространств имен попадает в одно и то же пространство. Например, вы може-
те иметь программный файл с двумя пространствами имен:

 

 


Второе объявление пространства имен с тем же именем — это просто продолже-
ние первого, поэтому вы можете обращаться к именам первого блока пространства
имен из второго без необходимости квалифицировать их. Все они находятся в одном
пространстве имен. Конечно, обычно вы не будете намеренно организовывать ис-
ходный файл подобным образом, но такое может случиться естественным образом с
заголовочными файлами, которые вы включаете в свою программу. Например, у вас
может быть нечто вроде:




#include <iostream> // Содержимое - в пространстве имен std

#include "myheader.h" // Содержимое - в пространстве имен myStuff

#include <string> // Содержимое - в прос транстве имен std

//и так далее...

Здесь iostream и string — заголовочные файлы стандартной библиотеки ISO/
ANSI С++, a myheader. h — заголовочный файл, содержащий ваш программный код.
То есть вы имеете ситуацию с пространствами имен, которая в точности повторяет
предыдущий пример.

Все это должно дать вам представление о том, как работают пространства имен.
Можно еще очень много сказать о пространствах имен, но того, что я показал здесь,
достаточно, чтобы вы ухватили суть и были готовы без проблем узнать больше при
необходимости.

Обратите внимание, что две формы директивы iinclude в предыдущем фрагменте кода
заставляют компилятор искать включаемые файлы разными способами. Когда вы специ-
фицируете включаемый файл между угловыми скобками, то тем самым сообщаете компиля-
тору, что он должен искать его в пути, указанном опцией компилятора /I, а не найдя там,
обратиться к переменной окружения INCLUDE. Эти пути указывают на библиотечные фай-
лы С++, поэтому такая форма зарезервирована для библиотечных заголовков. Переменная
окружения INCLUDE указывает на папку, содержащую библиотечные заголовки, а опция
/1 позволяет специфицировать дополнительные каталоги, содержащие библиотечные за-
головки. Когда же имя файла указано между двойных кавычек, компилятор ищет папку, где
находится файл, в котором встретилась данная директива iinclude. Если там файл не
найден, поиск продолжается в библиотечных каталогах.

 

Программирование на C++/CLI

C++/CLI предлагает множество расширений и дополнительных возможностей
вдобавок к тем, которые рассматривались до сих пор в этой главе. Прежде чем по-
грузиться в детали, я представлю краткий перечень этих дополнительных возможно-
стей. Итак, ниже перечислены дополнительные возможности C++/CLI.

□ Все фундаментальные типы данных ISO/ANSI, о которых я говорил ранее, мо-
гут быть использованы в программах C++/CLI, но здесь они имеют некоторые
дополнительные свойства в определенных контекстах, которые будут раскры-
ты позже.

□ C++/CLI предоставляет собственный механизм клавиатурного ввода и вывода
в командную строку консольных программ.

□ C++/CLI предоставляет операцию saf ecast, которая гарантирует, что опера-
ция приведения приведет к генерации проверяющего кода.

□ C++/CLI предоставляет альтернативную возможность перечисления на основе
классов и обеспечивает большую гибкость, чем объявление enum из ISO/ANSI
С++, которое вы видели ранее.

Вы узнаете больше о ссылочных типах классов CLR в начале главы 4, но поскольку
я уже представил глобальные переменные родного С++, то здесь отмечу, что перемен-
ные ссылочных типов классов CLR не могут быть глобальными.

Я бы хотел начать с обзора фундаментальных типов данных C++/CLI.


124 Глава 2

 

Специфика C++/CLI: фундаментальные типы данных

Вы можете и должны использовать имена фундаментальных типов ISO/ANSI С++
в своих программах C++/CLI, и с арифметическими операциями они работают точно
так же, как вы видели в родном С++. Кроме того, в C++/CLI определены два дополни-
тельных целочисленных типа, которые описаны в табл. 2.8.

Чтобы специфицировать литералы типа long long, необходимо добавлять LL или
11 к целому значению. Например:

Литерал типа unsigned long long указывается добавлением ULL или ull к целому
значению:

Хотя все операции с фундаментальными типами, что вы видели ранее, работают
аналогичным образом с C++/CLI, имена фундаментальных типов в программах С++/
CLI имеют другой смысл и предоставляют дополнительные возможности в некото-
рых ситуациях. Фундаментальный тип в программе C++/CLI — это класс типа значе-
ния, и может вести себя как обычное значение или как объект, если обстоятельства
того требуют.

Внутри языка C++/CLI каждый фундаментальный тип ISO/ANSI отображается
на класс типа значения, определенный в пространстве имен System. То есть в про-
грамме на C++/CLI имена фундаментальных типов являются сокращениями для ас-
социированных с ними классовых типов. Это позволяет трактовать значение фунда-
ментального типа как просто значение либо, при необходимости, как автоматически
преобразованный объект ассоциированного типа класса. Фундаментальные типы, объ-
ем занимаемой ими памяти и соответствующие им типы классов показаны в табл. 2.9.

По умолчанию тип char эквивалентен signed char, поэтому ассоциированный с
ними класс типа значения — System:: SByte. Обратите внимание, что вы можете из-
менить умолчание char на unsigned char, установив опцию компилятора / J, и в этом
случае ассоциированным типом класса будет System::Byte. System — это корневое
пространство имен, в котором определены классы типа значения C++/CLI. Существует
множество других типов, определенных в пространстве имен System — такие как
String — для представления строк, с которым вы познакомитесь в главе 4. C++/CLI
также определяет в пространстве имен System класс типа значения System:: Decimal.
Переменные этого типа могут содержать десятичные числа с точностью до 28 знаков.

Как я уже говорил, то, что классы типа значений ассоциированы с каждым фун-
даментальным типом, добавляет новые возможности для переменных этих типов в
С++/CLI. Когда необходимо, компилятор выполняет автоматические преобразования
от исходного значения в ассоциированный тип класса и обратно; этот процесс назы-
вается упаковкой (boxing) и распаковкой (unboxing) соответственно. Это позволя-
ет переменной любого из этих типов вести себя как простое значение либо как объ-
ект — в зависимости от обстоятельств. Подробнее о том, когда и как это происходит,
вы узнаете в главе 6.



Поскольку имена фундаментальных типов ISO/ANSI С++ служат псевдонимами
для имен классов в программе С++/СЫ, в принципе вы можете использовать в коде
C++/CLI и те, и другие. Например, вы уже знаете, как записывать операторы, создаю-
щие целые переменные и переменные с плавающей точкой:

 

 


Вы можете использовать имена классов, соответствующие фундаментальным ти-
пам, и компилировать программу без каких-либо проблем:

 

 


Хотя это совершенно законно, все же в своем коде вы должны использовать име-
на фундаментальных типов, такие как int и double, вместо имен классов System::
Int32 и System:: Double. Причина в том, что отображение имен фундаментальных
типов на имена классов и обратно — это функциональность компилятора Visual С++.
Другие компиляторы не обязательно реализуют такие преобразования. Имена фунда-
ментальных типов фиксированы в стандарте языка C++/CLI, но отображение боль-
шинства их на имена классов зависит от реализации. Тип long в Visual С++ 2005 ото-
бражается на тип Int32, но вполне возможно, что в какой-то другой реализации он
будет отображаться на тип Int 64.

Возможность представления данных фундаментальных типов в виде объектов
классов типа значений — важное средство C++/CLI. В ISO/ANSI С++ фундаменталь-
ные типы и типы классов совершенно различны, в то время как в C++/CLI все дан-
ные сохраняются в виде объектов классов — как классов типов значений, так и клас-
сов ссылочных типов. О классах ссылочных типов вы узнаете в главе 7.

Теперь попробуем написать консольную программу CLR.


Практическое занятие


Консольная программа CLR


Создайте новый проект и выберите тип проекта CLR и шаблон CLR Console
Application (Консольное приложение CLR). Затем вы можете ввести имя проекта
Ех2_12, как показано на рис. 2.13.

Рис. 2.13. Создание консольного приложения CLR

Когда вы щелкнете на кнопке ОК, мастер Application Wizard сгенерирует проект,
содержащий следующий код:

Уверен, что вы обратили внимание на то, что находится между скобками вслед за
main. Это связано с передачей значений функции main (), когда вы инициируете вы-
полнение программы из командной строки, но об этом вы узнаете больше, когда мы
обратимся к теме функций. Если вы скомпилируете и запустите проект по умолча-
нию, то увидите, что он выдаст в командную строку "Hello World". А теперь преоб-
разуем эту программу в CLR-версию примера Ех2_02, чтобы посмотреть, насколько
она будет похожа. Чтобы сделать это, модифицируйте код Ех2_12. срр следующим об-
разом:


 
 

Новые строки выделены полужирным, и они заменяют две автоматически сгене-
рированные строки в main (). Теперь вы можете скомпилировать и запустить проект.
Программа должна выдать следующий вывод:

Описание полученных результатов

Единственное серьезное отличие — способ вывода. Определения переменных и
вычисления — те же самые. Хотя вы применяете те же имена типов, что и в версии
этого примера ISO/ANSI С++, это не то же самое. Переменные apples, oranges и
fruit имеют тип C++/CLI , который специфицирован как int, и

они обладают некоторыми дополнительными возможностями по сравнению с типом
ISO/ANSI. Здесь переменные в некоторых случаях могут вести себя либо как про-
стые значения, либо как объекты. Если вы хотите подтвердить, что в данном случае
Int32 — то же самое, что int, то можете заменить имя типа int на Int32 и переком-
пилировать пример. Он должен работать точно таким же образом.

Очевидно, что следующая строка кода выдает первую строку вывода:

 


WriteLine () — это функция C++/CLI, которая определена в классе Console про-
странства имен System. Подробнее о классах вы узнаете в главе 6, а пока что скажу,
что класс Console предоставляет стандартные потоки ввода и вывода, соответствую-
щие клавиатуре и командной строке консольного окна. То есть функция WriteLine ()
пишет все, что находится между скобками, следующими за ее именем, в командную
строку, добавляя при этом символ новой строки, чтобы переместить курсор в начало
следующей строки экрана. То есть предыдущий оператор выводит в командную стро-
ку текст "ХпАпельсины — не единственные фрукты... ". Буква L, предшествующая
строке, указывает на то, что это строка, состоящая из "широких" символов, где каж-
дый занимает 2 байта.

Функция Write () класса Console — это, по сути, то же самое, что функция
WriteLine (), с единственным отличием — она не добавляет к указанному выводу сим-
вол новой строки. Поэтому вы можете использовать Write (), когда нужно вывести
два или более элемента данных в одну строку несколькими отдельными операторами.

Значение, которые вы помещаете между скобками, следующими за именем функ-
ции, называются аргументами. В зависимости от того, как написана функция, она


128 Глава 2

 

может принимать при ее вызове ноль, один или более аргументов. Когда вам нужно
передать более одного аргумента, их следует разделять запятыми. Функции вывода
класса Console заслуживают более детального рассмотрения, поэтому я опишу их
подробно.

Вывод командной строки C++/CLI

Вы увидели в последнем примере, как можно использовать методы

и для вывода строк или других элементов данных

в командную строку. Между их скобками можно поместить переменную любого из ти-
пов, которые вам встречались в этой книге до сих пор, и ее значение будет выведено
в командную строку. Например, вы можете написать следующие операторы для выво-
да выходной информации о количестве пакетов:

Выполнение этих операторов даст следующий вывод:

Весь вывод уместился в одной строке, потому что в первых двух операторах ис-
пользуется функция Write (), которая не выводит символ новой строки после дан-
ных. В последнем операторе применяется функция WriteLine (), которая при выводе
присоединяет символ новой строки к переданным данным, поэтому весь последую-
щий вывод окажется в новой строке.

Выглядит довольно утомительным использовать целых три оператора, чтобы по-
лучить одну строку вывода, поэтому не удивительно, что предусмотрен и другой спо-
соб. Это средство связано с форматированием вывода в командной строке программ
.NET Framework, и об этом мы поговорим в следующем разделе.

Специфика C++/CLI — форматирование вывода

Обе функции — и |, и — имеют сред-

ства управления форматом вывода, и этот механизм работает совершенно одинаково
в обеих функциях. Проще всего понять его на примере. Во-первых, посмотрим, как
можно одним оператором получить вывод, который в предыдущем примере форми-
ровался тремя операторами:

 


Здесь второй оператор даст тот же вывод, что вы видели в предыдущем разделе.
Первый аргумент — строка Ь"Имеется {0} пакетов. ", в кото-

рой фрагмент " {0}" отмечает место, куда будет помещен второй аргумент. Этот фраг-
мент заключает в себе форматную строку, применяемую для вывода второго аргумен-
та, хотя в данном случае она предельно проста, и состоит из одного нуля. Аргументы,
следующие в функции за первым, пронумерованы по поряд-

ку, начиная с нуля:


Данные, переменные и вычисления 129

 

Таким образом, ноль между фигурными скобками в предыдущем фрагменте кода
указывает на то, что аргумент packageCount должен заменить {0} в строке-аргументе
при выводе ее на консоль.

Если вы захотите вывести еще и вес пакетов вместе с числом, то можете написать
так:

 


Теперь оператор вывода имеет три аргумента, и ко второму и третьему в формат-
ной строке выполняется обращение по номерам 0 и 1 соответственно. Поэтому это
даст следующий вывод:

Вы также можете написать оператор, который выведет два последних аргумента в
обратном порядке, как здесь:

 


Теперь в форматной строке 0 ссылается на переменную packageWeight, а 1 — на
packageCount; вывод получится точно таким же, как прежде.

У вас также есть возможность специфицировать, как будут представлены данные в
командной строке. Предположим, что вам нужно вывести значение с плавающей точ-
кой packageWeight с двумя десятичными разрядами после точки. Это можно сделать
следующим образом:

 


В подстроке 1: F2 двоеточие отделяет значение индекса — 1, идентифицирующее
аргумент, от следующей за ним спецификации формата — F2. Буква F в спецификации
формата указывает, что вывод должен быть в форме "±ddd.dd..." (где d представляет
десятичную цифру), а 2 — количество разрядов после точки. Вывод этого оператора
будет таким:

 


Вообще вы можете писать спецификацию формата в виде {n,w:Ахх}, где п — зна-
чение индекса, указывающее номер аргумента, следующего за форматной строкой,
w — необязательная спецификация ширины поля, А — односимвольная спецификация
формата значения, а хх — необязательное одно- или двузначное число, задающее точ-
ность вывода значения. Спецификация ширины поля — целое со знаком. Значение
будет выровнено вправо в поле w, если ширина положительна, и влево — если отрица-
тельна. Если значение занимает меньше знаков, чем указано в w, то вывод дополняет-
ся пробелами; если значение не помещается в ширину w, то спецификация ширины
игнорируется. Вот еще один пример:

 

 


Количество пакетов выводится в поле шириной 3 знака, а вес — в поле шириной 5,
поэтому в результате получим:


130 Глава 2

 

Существуют и другие спецификаторы формата, которые позволяют вам предста-
вить различные типы данных различными способами. Некоторые из наиболее часто
используемых спецификаторов формата перечислены в табл. 2.10.

 

Ниже приведен демонстрационный пример вывода консольной программы CLR,
вычисляющий цену коврового покрытия.

Вывод этой программы должен быть таким:


 
 

Описание полученных результатов

Размеры комнаты указаны в футах, в то время как цена коврового покрытия
определяется площадью в квадратных ярдах, поэтому вы определяете константу
f eetPerYard, чтобы использовать ее при пересчете футов в ярды. В выражении
преобразования каждое измерение получается в результате деления значения типа
double на int. Компилятор вставит код для преобразования типа int в double, преж-
де чем выполнит умножение. После преобразования размеров комнаты в ярды выпол-
няется вычисление цены коврового покрытия перемножением размеров в ярдах, что-
бы получить площадь, и умножением этой площади на цену за квадратный ярд.

Операторы вывода используют спецификатор формата F2, чтобы ограничить вы-
ходные значения двумя десятичными знаками после точки. Без этого в выводе мо-
жет получиться больше знаков, что нежелательно, особенно когда речь идет о цене.
Чтобы увидеть разницу, можете удалить спецификатор формата.

Обратите внимание, что оператор вывода площади использует арифметическое
выражение во втором аргументе функции WriteLine (). Компилятор сначала вычис-
лит это выражение, а затем передаст результат в виде аргумента функции. В общем
случае вы всегда можете использовать выражение в качестве аргумента функции — до
тех пор, пока тип полученного результата согласуется с типом параметра функции.

Клавиатурный ввод в C++/CU

Возможности ввода с клавиатуры, которые имеются у консольных программ.NET
Framework, несколько ограничены. Вы можете прочесть полную строку ввода как тек-
стовую строку, используя для этого функцию Console::ReadLine (), или же вы мо-
жете прочесть отдельный символ, применяя для этого функцию Console::Read ().
Можно также прочесть нажатие клавиши функцией Console:: ReadKey ().
Функция используется следующим образом:

 

 


Это читает полную входную строку текста, завершенную нажатием клавиши
<Enter>. Переменная line имеет тип и хранит ссылку на строку, которая

получается в результате выполнения функции . Маленький

символ , который следует за именем типа String, указывает, что это — дескриптор
(handle), который ссылается на объект типа String. О типе String и дескрипторах
объектов String вы узнаете в главе 4.

Оператор, читающий один символ с клавиатуры, выглядит следующим образом:

С помощью функции Read () вы можете читать входные данные символ за симво-
лом, и затем анализировать прочитанные символы и преобразовывать их в соответ-
ствующие числовые значения.

Функция < возвращает нажатую клавишу в виде объекта класса

ConsoleKeylnf о, который представляет собой класс типа значения, определенный в
пространстве имен System. Вот оператор чтения нажатой клавиши:


132 Глава 2

 

Аргумент true функции ReadKey () подавляет отображение нажатой клавиши в
командной строке. Аргумент false (или отсутствие аргумента) заставляет функцию
отображать символ нажатой клавиши. Результат выполнения функции сохраняется
в keyPress. Чтобы идентифицировать символ, соответствующий нажатой клавише
(или клавишам), необходимо использовать выражение keyPress.KeyChar. Таким об-
разом, чтобы вывести сообщение, показывающее символ нажатой клавиши, нужно
воспользоваться следующим оператором:

 

 


Нажатая клавиша идентифицируется выражением keyPress.Key. Это выражение
ссылается на значение из перечисления C++/CLI (с которым вы вскоре познакоми-
тесь), идентифицирующее нажатую клавишу. Об объекте ConsoleKeylnf о можно ска-
зать еще много чего. И в этой книге мы еще к нему вернемся.

Хотя то, что в консольных программах C++/CLI нет форматирования ввода, мо-
жет показаться неудобным во время изучения, на практике это ограничение не суще-
ственно. Почти все реальные программы, которые вам придется писать, принимают
ввод через компоненты окна, поэтому обычно нет необходимости читать данные из
командной строки.

 

Применение safe_cast

Операция safecast предназначена для явных приведений типов в среде CLR. В
большинстве случаев вы можете без проблем использовать staticcast для приведе-
ния одного типа к другому в программах C++/CLI, но поскольку есть исключения, ко-
торые приводят к сообщениям об ошибках, лучше все-таки использовать safecast.
Приведение saf ecast применяется точно так же, как staticcast. Например:

 


Последний оператор приводит каждое из значений типа double к типу int перед
тем, как сложить их и присвоить результат wholenumber.

Перечисления C++/CLI

Перечисления в программах C++/CLI существенно отличаются от тех, что приме-
няются в программах ISO/ANSI С++. Прежде всего, они и определяются в C++/CLI
иначе:

Этот оператор определяет перечислимый тип Suit (масть), и переменным типа
Suit можно присваивать значения только из перечня, заданного в определении —
Hearts, Clubs, Diamonds и Spades. Когда вы обращаетесь к константам из перечис-
ления С++/СЫ, вы всегда должны квалифицировать их именем типа перечисления.
Например:

Этот оператор присваивает значение Clubs из перечисления Suit переменной по
имени suit. Символ , отделяющий имя типа Suit от имени перечислимой констан-
ты Clubs — это операция разрешения контекста, которая указывает на то, что Clubs
существует внутри контекста перечисления Suit.


Дата добавления: 2015-11-04; просмотров: 25 | Нарушение авторских прав







mybiblioteka.su - 2015-2024 год. (0.032 сек.)







<== предыдущая лекция | следующая лекция ==>