КулЛиб - Классная библиотека! Скачать книги бесплатно 

Visual C++.NET для "чайников" [Майкл Хаймен] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]
ba
ynd
MB
c
ih
a
e
l
H
y
m
a
n
obArnson

Hungry Minds™

HUNGRYMINDS,INC.
Bes-tSe
nilgfiooks•Dg
ita
i lDownolads•eB
-ooks•AnswerNew
t okrs•e-Newsele
trs•Ba
rndedWebSe
tis-e-Learnnig
NewYork,NY•Ce
l vea
l nd,OH•Indianapolis,IN

Майкл Хаймен
Боб Арнсон

ДИАЛЕКТИКА
Москва • Санкт-Петербург • Киев
2002

ББК32.973.26-018.2.75
Х15
УДК 681.3.07

Компьютерное издательство ''Диалектика"
Зав. редакцией В.В. Александров
Перевод с английского и редакция П.А. Минько

1

По общим вопросам обращайтесь в издательство "Диалектика'
по адресу: infoldialcktika.com. http://vvww.dialektika.coni

Хаймен, Майкл, Арнсон, Боб.
Х15 Visual C++.NET для "чайников". : Пер. с англ. — М. : Издательский дом "Вильяме".
2002. — 288 с. : ил. — Парал. тит. англ.
ISBN 5-8459-0326-2 (рус.)
Итак, вы решили серьезно взяться за Visual C++ .NHT. Это хорошая идея, ведь вы в
действительности убиваете сразу трех зайцев: в ваших руках оказывается мощный, полезный
и широко распространенный инструмент. С языком С+-1- можно сделать очень многое. С его
помощью созданы такие продукты, как Hxeel и Access. 'Jror язык также применяется при
разработке управленческих информационных систем и систем целевого назначения,
используемых для анализа деятельности предприятий и принятия решений в сфере
управления бизнесом. И. конечно же, целые армии хакеров и не только хакеров используют
C++ для создания инструментов, утилит, игр и шедевров мультимедиа. Знания, которые вы
получите, изучив язык C++ .NHT. позволят создавать не просто приложения, а приложения,
работающие в разных операционных системах. Возможности этого языка практически не
ограничены, и вы сами в этом убедитесь, прочитав эту книгу.
Книга предназначена для начинающих программистов.
ББК 32.973.26-018.2.75
Все названия программных продуктов являются зарешстрнрованнымиторголыми чарками соответствующих фирм.
Никакая чисть настоящею издания ни в каких целях не может быть воспрошведена в какой бы го ни было
форме и какими бы ю ни было средствами, будь ю электронные или механические, включая
фотокопирование и запись на магнитный носитель, если на эю fiei письменного разрешения издательства
Hungry Minds. Inc.
Copyright © 2002 by Dialektika Computer Publishing.
Original English language edition copyright £> 2002 by Hungry Minds, Inc.
All rights reserved including the right of reproduction in whole or in part in any form.
This edition published by arrangement with the original publisher. Hungry Minds, Inc.
For Dummies and Dummies Man are trademarks under exclusive license to Hungry Mindi, Inc. Used by permission.
ISBN 5-8459-0326-2 (рус )
ISBN 0-7645-0868-7 (англ )

О Компьютерное изд-во "Диалектика". 2002
V: Munarv Minds, Inc.. 2002

Оглавление
Введение

14

Глава 1. Что представляет собой пакет Visual C++ .NET

19

Глава 2. Что такое программа

25

Глава 3. Приступаем к созданию программ

35

Глава 4. Принятие решений — дело серьезное

49

Глава 5. Хороший редактор — что еще нужно бывалому программисту? 55
Глава 6. Компиляция программ, или Первые трудности

65

Глава 7. Типы данных — это серьезно

71

Глава 8. Использование переменных

81

Глава 9. Структуры данных

85

Глава 10. Выразите свои желания

89

Глава 11. Ключевые слова — ключ к диалогу с компьютером

105

Глава 12. Внимание! Повышенная функциональность

117

Глава 13. Указатели

129

Глава 14. Масса информации? Используйте массивы!

155

Глава 15. Пришел, увидел, применил

165

Глава 16. Через тернии к... работающей программе

171

Глава 17. Смотрите на мир объективно

189

Глава 18. Конструкторы и деструкторы

209

Глава 19. Наследование

219

Глава 20. Исключительные ситуации

237

Глава 21. Потоки данных

249

Глава 22. Создаем пользовательский интерфейс

255

Глава 23. Десять синтаксических ошибок

265

Глава 24. Вторая десятка синтаксических ошибок

271

Глава 25. Десять функций .NET

277

Предметный указатель

280

Содержание
Введение
Часть I. Первое знакомство с Visual C++ .NET
Глава 1. Что представляет собой пакет Visual C++ .NET
Инструменты Visual C++
Компилятор, запускающий тысячи программ
Отладчик программ
Интегрированная среда разработки программ
Библиотеки - хранилища электронных инструментов
Эти разумные утилиты
Помощь, которая всегда рядом
Не знаете с чего начать - просмотрите примеры программ
Управляемые и неуправляемые программы

17
19
19
20
20
21
21
22
22
22
23

Глава 2. Что такое программа

25

Введение в программирование

25

Главная функция программы

26

Стандартные подпрограммы

27

Для чего создаются программы

28

А теперь немного теории
Что такое объект и с чем его едят
Инкапсуляция
Наследование
Полиморфизм

31
31
32
32
33

Глава 3. Приступаем к созданию программ

35

Зачем нужны исходные файлы

35

Как все это выглядит на практике
С чего начинается выполнение программы
Как организовать диалог с пользователем
Не скупитесь на комментарии
Исходный код программы HelloWorld
Пару штрихов к программе Hello World
Забудем о . NET
Отображение информации на экране
Печать с новой строки
Обратная связь: получение ответа
Использование библиотечных функций

36
38
39
40
41
42
42
43
44
45
45

Итак, займемся делом

46

Глава 4. Принятие решений - дело серьезное
Правильное решение может сделать вас счастливым
Файлы проектов сделают вашу жизнь проще

6

14

49
49
50

Содержание

Решения и проекты
Построение программ
Определение параметров нового проекта
Добавление файлов к проекту

50
50
50
52

Что может окно Solution Explorer

53

Глава 5. Хороший редактор — что еще нужно бывалому программисту?
Коды существуют для того, чтобы их редактировать

55
55

Приемы редактирования

57

Коды бывают разные - черные, белые, красные

59

Помощь, которая всегда рядом

60

Навигация по просторам программы

60

Сокрытие и отображение кодов

61

Поиск и автозамена

62

Глава 6. Компиляция программ, или Первые трудности

65

Начать компилировать программу очень просто

65

Синтаксические ошибки: неужели их может быть так много?!
Предупреждения
Почему компилятор не исправляет ошибки самостоятельно

66
67
68

Компилировать можно по-разному

68

Часть II. Все, что вы хотели знать о C + + , но о чем боялись спросить
Глава 7. Типы данных - это серьезно

69
71

Строгие и нестрогие языки программирования

71

Объявление переменных

72

Наиболее часто используемые типы данных
Реже используемые типы данных
Обеспечение типовой безопасности
Функции преобразования типов

72
73
74
75

Константы - то, что никогда не меняется
Использование констант в кодах программы
Константы и борьба с ошибками
Строки как один из наиболее важных типов данных

76
77
78
78

Глава 8. Использование переменных
Именование переменных

81
81

Определение переменных

83

Инициализация переменных

83

Как сделать имя информативным

84

Глава 9. Структуры данных
Объявление структур

85
85

Использование этих загадочных структур

86

Использование одних структур для создания других

86

Структуры на практике

87

Содержание

7

Глава 10. Выразите свои желания
Можно ли "выражаться"?
Простые операторы
Более сложные операторы
Оператор ++
Оператор >>
Оператор «
Истина и ложь в логических выражениях
Оператор присвоения
Все об операторах
Работа с битами
Условный оператор
Приоритет операторов
Примеры работы операторов
Математические функции
Старый формат математических функций

Глава 11. Ключевые слова — ключ к диалогу с компьютером
Великолепная тройка: ключевые слова if, for и while
Условный оператор
Оператор for
Пример использования цикла for
Повторение ради повторения
Вычисление факториала
Оператор while
Ключевые слова switch и do
Оператор switch
Оператор do

Глава 12. Внимание! Повышенная функциональность
Некоторые вводные замечания
Создание функций
Использование аргументов
Функции, которые возвращают результат
И снова вернемся к факториалам
Рекурсия: спасибо мне, что есть я у меня
Если тип аргументов не определен . . .
Значения, установленные по умолчанию

Глава 13. Указатели
Почему указатели
Указатели и переменные
Что вы! Указатели - это очень сложно
Информация и ее адрес
Безымянные данные

8

89
89
89
90
91
91
92
92
94
96
97
98
99
101
101
103
105
106
106
109
109
110
110
112
112
113
114
117
117
118
119
120
122
124
127
128
129
129
130
131
131
131
Содержание

Связанный список - размер не ограничен
Использование указателей в C++
Дайте указателю адрес
Как получить значение, на которое ссылается указатель
Пример программы, использующей указатели
Изменение значения, на которое ссылается указатель
Изменение значений в структурах данных
Использование стрелки
Динамическое выделение памяти

132
133
134
134
134
136
136
136
136

Знимание - графика
Все цвета радуги
Перья для рисования
Кисточка для раскраски
Шрифты
Займемся рисованием
Связанные списки и графика
Как эта программа работает
Код программы

137
139
141
142
142
142
144
144
146

Вопросы безопасности
Освобождение памяти
Общее нарушение защиты
Генеральная уборка
Кое-что о строках

149
149
150
151
152

Подведем итог

153

Глава 14. Масса информации? Используйте массивы!

155

Массивы: познакомимся поближе

155

Это же "элементарно", Ватсон

156

Инициализация массива

157

Многомерные массивы

158

Класс ArrayList

159

Класс Stack

161

Перечислимые типы
Безопасность при использовании перечислимых типов
Одно маленькое "но"

162
162
163

Глава 15. Пришел, увидел, применил

165

Немного теории

165

Почему это так важно

166

Правила определения области видимости

Глава 16. Через тернии к... работающей программе

169

171

Синтаксические и логические ошибки

171

Процесс отладки программы

172

Отладчик плюс редактор - и все в наших руках

173

Содержание

9

Шаг за шагом: движение к цели

173
174

Посмотрим, что получилось
Не нравится значение - измените его
Торопитесь? Нет проблем!
Очень торопитесь?
Генеральная уборка
Итак, приступим
Так где же ошибка?
Что теперь?
Но в программе по-прежнему есть ошибка
Устранение ошибки
Последние штрихи

175
176
177
177
178
178
180
183
184
184
185

Остановись, мгновение!

Часть III. Экскурс в объектно-ориентированное программирование
Глава 17. Смотрите на мир объективно
Что такое классы и с чем их едят
Разберемся в деталях
Члены данных
Функции-члены
Объявление классов
Ограничение доступа
Защищенный доступ
Определение функций-членов
Что делать с готовыми классами?
Доступ к элементам класса
Имена, используемые функциями-членами
Немного практики
Управление памятью в неуправляемых программах
Функции доступа
Общие рекомендации

Глава 18. Конструкторы и деструкторы
Работа "до" и "после"
Подготовительный этап
Много конструкторов - много возможностей
Открытые и закрытые конструкторы
Заметание следов
Когда объект больше не нужен
Не забывайте также освобождать динамически выделяемую память
Классы внутри классов
Чтение кодов объектно-ориентированных программ

Глава 19. Наследование
Что происходит при наследовании кодов

10

187
189
189
190
190
190
191
191
192
192
193
193
194
194
198
203
207
209
209
209
210
212
212
212
214

215
216
219
219
Содержание

Наследование открытых, закрытых и защищенных элементов
Перегрузка функций-членов
Родительские связи
А теперь немного практики
Как процесс наследования отображается на конструкторах и деструкторах
Универсальный указатель
Защищенное и закрытое наследование
Виртуальная реальность
Тест на виртуальность
Декларация ваших виртуальных намерений
Когда без этого не обойтись
Абстрагирование от деталей
Искусство абстрагирования
Глава 20. Исключительные ситуации

221
221
222
222
224
224
225
226
227
227
228
231
232
237

Как это было раньше

237

Новый усовершенствованный способ обработки ошибок
Вот как это выглядит на практике
Гибкость - основное свойство механизма обработки исключений
Определение собственных исключительных ситуаций

238
239
241
241

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

242
245
246

Пять правил исключительного благополучия

247

Глава 2 1 . Потоки данных

249

.NET-классы I/O

249

Записали - прочитали
Повторное использование потоковых переменных
Записали - прочитали
Специальные команды, погружаемые в потоки данных
Формат передаваемых данных

250
251
252
253
253

Кое-что о работе с файлами

254

Глава 22. Создаем пользовательский интерфейс
Основной набор инструментов
Формы
Наследование форм
Настройка формы
Открытие формы
Обработка событий
Объекты, которые умеют реагировать
Хорошие менеджеры умеют делегировать свои обязанности
Объявление и определение обрабатывающей событие функции
Добавление делегата

Содержание

255
255
257
257
258
258
259
259
260
261
261

11

Часть IV. Горячие десятки

263

Глава 23. Десять синтаксических ошибок
Подключаемый файл не может быть найден

265

Пропущена точка с запятой

266

Не подключен заголовочный файл

266

Не обновлено объявление класса

267

Использование названия класса вместо названия объекта

267

После объявления класса не поставлена точка с запятой

268

В определении класса пропущено слово public:

268

Неправильно набранные имена переменных

268

Использование точки вместо стрелки и наоборот

269

Пропущена закрывающая фигурная скобка
Глава 24. Вторая десятка синтаксических ошибок
Конструкторы, которые не требуют аргументов

269
271
271

Незакрытые комментарии

272

Несоответствие типов данных

272

То, что работало в С, может не работать в C++

273

Использование ключевого слова void

273

Конструкторы для производных классов

274

Точка с запятой в конце строки #define

274

Отсутствие свободного места на диске

274

Так в чем же проблема

275

Глава 25. Десять функций .NET

277

Console: :WriteLlne

277

Console::ReadLJne

277

lnt32::Parse

277

Application::Run

278

Graphics->DrawLine

278

Color:

;FromArgb

278

Graphics->DrawString

278

lmage::FromFile

279

Form:;OnMouseMove

279

Controls->Add

279

Предметный указатель

12

265

280

Содержание

OSа£*но/га)с
Майкл Хаймен (Michael Hyman)— руководитель технического отдела компании
DataChannel. Ранее занимался вопросами медиатехнологий, XML и Internet. Майкл написал
множество книг по компьютерной тематике, среди которых Dynamic HTML For Dummies, Visual
J++ For Dummies и PC Roadkiil. Он имеет ученую степень по электронной инженерии и компьютерным наукам, полученную в Принстонском университете. В свободное время Майкл занимается серфингом, ходит в спортзал и стирает пеленки.
Боб Арнсон (Bob Arnson)— разработчик программного обеспечения в крупной компьютерной компании. Боб написал несколько других книг no Visual Basic, Visual C++ и Borland C++.
Также его статьи публиковались в журналах VB Tech Journal и VC++ Professional.

Майкл Хаймен посвящает эту книгу Мириам Бэс (Miriam Beth) и Габриеле Миа (Gabrielle Mia).
Боб Арнсон посвящает эту книгу Марио (Mario).

Особая благодарность моей жене Саре, которая неделями терпела мои ночные писательские бдения и. как следствие, мой уставший вид и сонные глаза. Также спасибо моим дочерям, которые иногда отвлекали меня от компьютера и возвращали на землю. Спасибо читателям, просто за то, что они интересуются такими вопросами, как программирование на Visual
C++. А также спасибо всем людям, которые приняли участие в создании этой книги.
Майкл Хаймен
Спасибо моим друзьям из Нью-Гемпшира и Мичигана за моральную помощь и поддержку, которая так нужна была мне в мои нелегкие писательские будни. Спасибо моим коллегам
по издательству, которые внесли свою посильную лепту в создание этой книги и благодаря
которым она имеет такое прекрасное оформление. Также хочу выразить благодарность компании Microsoft за новые технологические решения, появившиеся вместе со средой .NET.
Боб Арнсон;

Введение
Итак, вы решили серьезно взяться за C++ .NET. Это хорошая идея, поскольку вы убиваете
сразу трех зайцев: в ваших руках оказывается мощный, полезный и широко распространенный инструмент. С языком C++ можно сделать очень многое. С его помощью созданы такие
продукты, как Excel и Access. Этот язык также применяется при разработке управленческих
информационных систем и систем целевого назначения, используемых для анализа деятельности предприятий и принятия решений в сфере управления бизнесом. И, конечно же. целые
армии хакеров и не только хакеров используют C++ для создания инструментов, утилит, игр
н шедевров мультимедиа.
Знания, которые вы получите, изучив язык C++ .NET, позволят вам создавать не просто
приложения, а приложения, работающие в разных операционных системах. Возможности
этого языка практически не ограничены, и вы скоро сами в этом убедитесь.

Об э/ной книге
Скажем сразу, книга должна вам понравиться. Здесь не нужно читать главы одну за другой от начала и до конца, Если вы уже знакомы с Visual Studio, пропустите главы, посвяшенные ей. Просмотрите содержание книги и. если найдете что-то для себя незнакомое, просто
начните читать с этого места. Если же во время чтения вы вдруг уснете — не волнуйтесь, это
тоже бывает. Здоровый сон еше никому не повредил.
Тем, кто еще ничего не знает о C++, следует начинать чтение с самого начала, чтобы узнать
не только о C++, но и о Visual Studio .NET. Хотим сразу предупредить, что язык О + vie так
прост, как это может показаться вначале. Придется немного напрячь умственные способности,
чтобы осилить его. Вы столкнетесь с множеством правил, приемов, концепций и терминов.
Кроме того, в отличие от обычных языков программирования, О-+ реализует концепцию объектно-ориентированного программирования, что вообще является нишей не для слабых умов.
Вдобавок к сложностям C++ вас ожидает знакомство с массой инструментов Visual Studio
ЛЕТ. В целом приложение Visual C++, имеющие 120 Мбайт надстроек, библиотек и тому
подобных вещей, может внушать непосвященным благоговейный ужас. Вначале даже трудно
решить, с какого конца к нему подойти.
Но не все так страшно, когда у вас в руках эта книга. Она дает общее представление как о
С4--1-, так и о Visual C++. На вас не обрушится лавина технических деталей и терминов, вместо этого вы познакомитесь с наиболее важной информацией и общими концепциями. Причем это будет сделано в стиле, понятном любому нормальному человеку.
Эта книга не раскрывает всех возможностей языка C++. поскольку для этого понадобилось бы добавить в нее еше несколько сот страниц или набрать весь текст очень мелким
шрифтом. Но зато вы получите достаточно знаний для того, чтобы начать писать на языке
С—*- свои собственные программы.

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

Введение

14

UcxoqHbienfteqnocMUtcu
Чтобы прочитать и понять эту книгу, не обязательно иметь опыт программирования. Однако, если раньше вы уже пытались что-то программировать, например создавали макросы
для электронных таблиц или баз данных, вы будете чувствовать себя во время чтения книги
намного комфортнее тех, кто ранее с программированием вообще не сталкивался.
Если вы уже освоили BASIC, COBOL, Pascal или даже С, научиться программировать на
C++ для вас не составит большого труда. (Но если вы уже профессионал по C++, то, вероятно, купили не ту книгу-)
Вне зависимости от вашего программистского прошлого, мы все же предполагаем, что вы
знаете, как запускаются приложения Windows и что представляют собой файлы и программы,
Также мы предполагаем, что на вашем компьютере установлены .NET и Visual Studio .NET
(если вы, конечно, хотите сами попробовать набрать приведенные в книге коды и проверить,
как они работают).

Мак организована э*па книга
Книга состоит из четырех частей. В первой части дается краткий обзор Visual C++. Вы
познакомитесь с основными возможностями этого языка программирования и научитесь ими
пользоваться.
Во второй части дается обзор основ программирования на C++ (обратите внимание, что
многие рассмотренные положения будут справедливы также и для языка С). Здесь же вы узнаете, что такое программы .NET и как они создаются.
В третьей части вы окунетесь в мир объектно-ориентированного программирования и
расширите свои познания о среде .NET.
В четвертой, заключительной, части, вы найдете практические советы, позволяющие решить многие проблемы, наиболее часто возникающие у начинающих программистов.

используемые € книге
Пиктограммы— это маленькие картинки, акцентирующие ваше внимание на некоторых
важных вещах. Вот, что они обозначают.
Этой пиктограммой обозначаются технические подробности, которые, впрочем,
читать не обязательно.
Внимательно относитесь к информации, отмеченной такой пиктограммой! Обязательно постарайтесь ее запомнить. Это будет несложно, поскольку информация
дается в максимально сжатом и лаконичном виде. Если же вы ее проигнорируете,
потом об этом можете пожалеть.
Этой пиктограммой отмечены различные полезные советы, которые могут облегчить вашу программистскую жизнь и избавить от ненужных проблем.

15

Введение

Такой пиктограммой отмечено все, что касается неуправляемых программ, т.е.
!„•;_• 1 программ, написанных на старом добром C++, которым разработчики среды
.NET дали такое пренебрежительное определение.

дальше?
На Гавайи, Тайвань, Фиджи. Но только не туда, куда посылала Михаэля эго жена, обиженная тем, что на какое-то время он посвятил себя написанию этой книги.

Введение

16

Часть I

Первое знакомство
cVisualC++.NET

Л1Л

НЕ о VUUAI 6++ .

/3 э&ой
Здесь дается краткий обзор Visual C++, начиная с установочного пакета
и заканчивая описанием его отдельных инструментов.
Вы узнаете, как создаются программы, и сможете получить обшее
представление об основных принципах объектно-ориентированного
программирования. Также вы увидите, как можно использовать мастера
Visual C++ для создания программ .NET. Здесь же вы познакомитесь с
такими важными компонентами окружения Visual C++, как компилятор,
редактор кодоь и средство Solution Explorer.

Глава1

Что представляет собой пакет
Visual C++ .NET
В3tfU>u иаве...
> Что такое Visual C++
> Обзор возможностей Visual C++
> Разница между управляемыми и неуправляемыми программами
к, вы решили стать программистом на языке C++. Это действительно хорошая
идея, Освоив этот язык, вы дополните свое резюме, сможете создавать потрясающие
приложения и находить общий язык с множеством очаровательных людей. Но главное— вы
сможете сказать, что владеете самым великолепным и мощным языком программирования из
всех когда-либо созданных.

Мнс/и/и/асен/ныVisualC++
Не все качественные приложения занимают много места.
Увидев впервые установочный комплект Visual C++, вы, наверное, решите, что чего-то в
нем не хватает,— настолько он небольшой и компактный. Казалось бы, приложение, занимающее множество мегабайт памяти, должно иметь более внушительный вид. Однако такое
впечатление создается потому, что наиболее массивная часть почти каждого установочного
пакета— руководство пользователя — поставляется не в распечатанном, а в электронном виде. (Таким образом не только экономится место, но и спасается несколько деревьев.)
Открыв установочный пакет Visual C++, вы найдете в нем компакт-диск с программным
обеспечением. На нем содержится множество инструментов, участвующих в создании программ на языке C++:
| V компиляторы;
f V отладчики;
I S интегрированная среда разработки;
I •/ системы создания приложений;
;. S библиотеки;
i S утилиты Windows;
i S общие утилиты;
\{ S библиотека оперативной документации;
1 S примеры программ.
Далее в этой главе дается краткое описание этих инструментов с тем, чтобы вы имели о
них обшее представление и знали, для чего устанавливаете.

Глава 1. Что представляет собой пакет Visual C++ . NET

19

Компилятор, запускающий тысячи программ
Компилятор преобразует коды из вида, понятного программистам [исходные коды), к виду, воспринимаемому компьютером [выполняемые коды). В приложении Visual O + есть два
компилятора:
S

строковый компилятор;

•/ интегрированная среда разработки.
Когда вы будете изучать описание примеров, приведенных в книге, используйте интегрированный компилятор, поскольку его пользовательский интерфейс проще и удобнее.
Что представляет собой строковый компилятор
Строковый компилятор- это компилятор, не имеющий графического пользовательского интерфейса. Он
быстр, но не очень дружелюбен по отношению к пользователю. Чтобы обратиться к нему, нужно набрать
специального вида компактную инструкцию, подобную этой:
c l /FR /WX foo.cpp
В данном примере первый элемент является именем компилятора, который вы хотите использовать, второй и третий представляют собой команды, сообщающие компилятору, что нужно делать, и наконец последний - это имя файла, который должен быть откомпилирован. (Использовать строковый компилятор
вместо интегрированной среды разработки могут только те программисты, которые знают, например, что
команда /FR применяется для отображения окна просмотра информации, а команда /wx обозначает,
что все предупреждения нужно интерпретировать как ошибки. Те, для кого приведенная выше строка является просто набором букв, использовать строковый компилятор не могут.)
В старые добрые времена известны были только строковые компиляторы. Те, кто занимается программированием довольно длительное время, используют в основном строковые компиляторы, во-первых, по привычке, а вовторых, поскольку они предоставляют возможность дополнительного использования множества замечательных
инструментов. Те же, кто только начинает программировать на Visual C++, предпочитают интегрированную среду разработки программ, поскольку она намного понятнее и проше в использовании.

Отладчик программ
Если вы написали программу, состоящую более чем из двух или трех строк, у вас наверняка возникнут проблемы при се компиляции. Если же этого не произойдет, то вы либо великий программист, либо скопировали коды программы из этой книги.
Все возникающие при запуске программы проблемы можно разделить на две категории:
синтаксические ошибки и логические ошибки. Синтаксические ошибки случаются тогда, когда вы что-то неправильно набираете, забываете указать информацию, которая нужна компилятору, или некорректно использ\сте команду. Компилятор сам находит синтаксические
ошибки и указывает строки, где такие ошибки встречаются. Вам придется исправить все найденные синтаксические ошибки, иначе компилятор не сможет преобразовать набранные вами
коды в выполняемые.
Логические ошибки случаются тогда, когда неправильно разработана или реализована сама программа. Например, вы забыли набрать коды для обработки какой-то информации или
даете команду отобразить на экране не те данные, которые необходимы. Программы, содержащие логические ошибки, процесс компиляции проходят успешно, однако выполняются не
так, как вы хотите.
Рассмотрим это на примере. Допустим, вы создали программу, отслеживающую количество денег на вашем счете. Но при написании ее кодов забыли набрать команду, прибавляю-

20

Часть I. Первое знакомство с Visual C++ .NET

щую суммы, вносимые на счет. Или наоборот: забыли набрать команду, вычитающую из обшей суммы деньги, снимаемые со счета. Тогда сколько бы денег вы со счета не снимапи, состояние счета не изменялось бы (этакий вечный источник наличных). Это пример логической
ошибки. Вы забыли набрать один оператор, и в результате получилась хотя и симпатичная,
но работающая неправильно программа.
Исправить логические ошибки намного сложнее, чем синтаксические. Сложность заключается в их поиске и локализации места возникновения. Помочь справиться с этой проблемой
призван отладчик программы. Отладчик позволяет строка за строкой проследить за выполнением программы и просмотреть значения обрабатываемых при этом данных. Таким образом, обнаружив, что какая-то переменная приняла неверное значение, вы сразу же определите
место возникновения ошибки.

Интегрированная среда разработки программ
Пакет Visual C++ включает в себя интегрированную среду разработки (devent.exe), объединяющую множество разнообразных инструментов, значительно упрощающих процесс создания программ. Если вы используете среду разработки Visual C++. нет необходимости изучать и запоминать принципы работы каждого инструмента в отдельности.
Среда разработки Visual C++ состоит из таких основных компонентов:
IS
1S
|
1V
\
вS
|
I/
1
IS
^
\ -/
|

редакторы, позволяющие набирать и модифицировать исходные коды программы;
компилятор, выполняющий компиляцию кодов программы (на этом этапе отсейваются и исправляются все синтаксические ошибки);
отладчик, помогающий исправить логические ошибки и заставить программу работать так, как вы хотите;
диспетчер проектов, позволяющий легко создавать выполняемые подпрограммы
(файлы с расширением DLL и LIB);
обозреватель, позволяющий отследить связи между различными объектами объектно-ориентированных программ;
Visual-инструменты (мастера), с помощью которых можно легко создавать Windows-приложения;
списки свойств, которые помогают контролировать отображение и поведение объектов пользовательского интерфейса создаваемой программы.

Чтобы воспользоваться возможностями любого из этих компонентов, достаточно просто
выбрать команду из раскрывающегося меню и задать настройки в диалоговом окне. Это значительно упрощает процесс реализации сложных проектов, поскольку нет необходимости
изучать II применять множество не совсем понятных командных строк. Читая эту книгу, вы
будете иметь дело только со средой разработки Visual C++ и освоите при этом большое количество различных приемов программирования.

Библиотеки — хранилища электронных инструментов
Библиотеки — это наборы заранее созданных функций и классов, которые могут быть использованы для решения многих типичных задач. Приложение Visual С+-1- имеет несколько
библиотек. Они могут заметно облегчить процесс создания программ, поскольку позволяют
использовать готовые варианты вместо разработки собственных решений. Среда .NET, no
существ), является одной большой библиотекой. Библиотеки выполнения (обозначаемые аббревиатурой RLT — runtime libraries) содержат множество полезных функций, выполняющих
математические вычисления, обработку текстовых значений и многие другие задачи. Файлами библиотек RLT являются libc.lib, libcmt.lib и msvcrt.lib.

Глава 1. ЧТО представляет собой пакет Visual C++ .NET

21

О чем говорят названия файлов библиотек
Со временем вы обратите внимание на то, что одни и те же библиотеки реализованы в нескольких вариантах. Каждый вариант соответствует разным наборам опций, поддерживаемых библиотекой и обозначаемых буквой (или буквами), которая добавляется в начале или в конце названия файла библиотеки.
Например, название mfc70d.lib обозначает, что это файл библиотеки MFC, выполненной в варианте для
отладчика (d - debuggable version).
Вот список букв, используемых для обозначения версий библиотек;
•/
•/
s
s
s

D Debuggable (Отладка);
МТ Multithreacl (Многопоточные процессы);
О OLE (Технология связывания и внедрения объектов);
S Static (Статические объекты);
U Unicode (Уникоды).

Эти разумные утилиты
Visual C++ содержит набор утилит, позволяющих проследить за выполнением .NET- и
Windows-программ. Обычно только опытные программисты пользуются услугами утилит,
наиболее важной среди которых является Spy+-. Во время выполнения программы она отображает на экране информацию о классах, применяемых этой программой. Вы можете использовать ее для отслеживания не только своих, но и чужых программ. Другие утилиты созданы для упрощения процесса программирования. В основном они рассчитаны на опытных
программистов и предназначены для использования совместно с инструментами командной
строки. Наиболее часто используемой среди них является утилита NMAKE. Но вы можете
также найти для себя парочку полезных утилит, не требующих умения работать с командной
строкой. Например, утилита W1NDIFF может сравнивать между собой содержимое двух файлов и находить в них отличия. Утилита ZOOM1N позволяет изменять размеры отображаемых
объектов таким образом, чтобы они выглядели на экране именно так, как вы хотите.

Помощь, которая всегда рядом
Все. что есть в пакете Visual C++ (компиляторы, отладчики, среда разработки, библиотеки.
\тилиты), должно сопровождаться документацией. Однако, чтобы не делать установочный пакет слишком громоздким, компания Microsoft поставляет документацию в электронном виде.
Причем эта документация организована в виде оперативной справочной системы, позволяющей
быстро находить нужную информацию и читать ее, не выходя из среды разработки программ.
Справочная система может быть открыта также в отдельном окне. В самом крайнем случае вы
можете лаже подключить дополнительный монитор к своей системе и постоянно иметь перед
глазами справку об инструментах и возможностях, используемых в данный момент.

Не знаете с чего начать — просмотрите примеры программ
Пакет Visual C++ включает в себя множество примеров готовых программ, воспользовавшись которыми вы сможете быстрее и проще создавать собственные программы. Некоторые из них демонстрируют определенные приемы программирования, другие являются готовыми вариантами решения каких-то задач, например обработки текстовой информации. Самое лучшее в этих программах то, что их коды можно копировать и вставлять в коды своих
программ. Это сохранит ваше время и силы и позволит сосредоточить внимание на решении
более специфических проблем.

22

Часть I. Первое знакомство с Visual C++. NET

Управляемые и
Visual C++ позволяет создавать программы как для среды Windows, так и для среды Microsoft .NET. В этой книге основное внимание уделяется вопросам разработки приложений для
среды .NET, но будет также рассмотрено и создание приложений для Windows. Среда .NET,
разработанная компанией Microsoft, предназначена для создания программ, работающих в Internet. Она включает в себя язык CLR (Common Language Runtime) и библиотеку классов.
CLR является основой среды .NET-— все работает под его управлением. CLR следит за
выполнением кодов и обеспечивает предоставление программам всех сервисов нижнего
уровня, таких, например, как управление памятью. Библиотека классов является пртожением среды .NET и содержит в себе набор готовых классов, которые вы можете использовать
при создании объектно-ориентированных программ. (О том, что такое классы и для чего они
нужны, вы узнаете чуть позже.)
Чтобы работать в среде .NET, программа должна быть изначально созданной для этой цели. Это подразумевает использование при ее создании классов .NET, но главное — она должна быть откомпилирована для работы в этой среде. Visual C++ может удовлетворить оба требования. Коды программы, которая может работать в среде .NET, называются управляемыми.
,_,,.( |

Коды программ, написанных для работы в среде Windows и не предназначенных
для использования в среде .NET, принято называть неуправляемыми.

it., К--А

Если вы не используете при разработке программы систему создания приложений, снабдить
программу хорошим пользовательским интерфейсом будет отнюдь не просто. Пользователям
очень нравятся программы с качественным интерфейсом, однако программистам приходится
потратить немало времени и усилий, чтобы создать его. Например, чтобы создать небольшую
программу для Windows, содержащую несколько пунктов меню и отображающую на экране сообщение "Hello World!", потребуется набрать от двух до четырех тысяч строк с кодами.
(Программисты называют между собой такие программы Hello World. По тому, насколько легко
или сложно создать программу Hello World, определяют, насколько проста и успешна та или
иная система программирования.) Это действительно так, если не использовать при создании
программы библиотеку классов среды .NET. Только программисты, помешанные на командах
нижнего уровня, занимаются подобными глупостями, все же остальные используют среду разработки .МЕТ. И сложность даже не в том, что нужно набирать эти четыре тысячи строк, а в
том. что нужно знать и помнить тысячи команд, используемых для управления средой Windows.
Это, пожалуй, не та информация, которой стоит засорять голову.
Большинство программ на порядок сложнее, чем Hello World, и при их создании приходится решить множество задач, прежде чем они заработают так, как нужно. Например, к их
числу относятся такие задачи:
IS

определение алгоритма получения сообщений Windows;

х S определение комбинаций клавиш, нажатием которых будут вызываться эти сообщения;
| S

определение, в какой части программы будут храниться полученные сообщения;

; S определение, какие другие части программы выполняются в данный момент;
., S

регистрация имен разных частей программы.

Обратите внимание, что ни одно из этих действий не имеет никакого отношения к выводу
чего-либо на экран.

Глава 1. Что представляет собой пакет Visual C++ . NET

23

Системы создания приложений позволяют автоматически решать эти и многие другие задачи. Например, приступив к созданию программы, вы можете воспользоваться классом System.Windows.Forms.Application, содержащим в себе все колы, необходимые для начала написания программы. В частности, этот класс содержит коды, необходимые для создания окон.
Эти и подобные им классы автоматически вставляют в коды вашей программы типичные
фрагменты, что дает возможность сосредоточиться на решении более важных задач.
Visual C++ содержит также другие системы создания приложений, например библиотеку
MFC (Microsoft Foundation Classes), включающую в себя классы C++. используемые при создании Windows-программ. Библиотека ATL {Active Template Library) содержит шаблоны C++,
используемые при создании объектов для модели COM (Component Object Model). Однако
системы ATL и СОМ настолько сложны, что их использование вызывает трудности даже у
хакеров со стажем, а потому в данной книге рассматриваться не будут.

24

Часть I. Первое знакомство с Visual C++ .NET

Глава2

Что такое программа
Вэ/Лой
>

Основы программирования

> Выражения, переменные, комментарии и библиотеки
>

Чтение программ

> Основы объектно-ориентированного программирования

м< этой главе дается краткий оозор основных этапов создания программ: проектирова'•^ ния, написания, компиляции и отладки. Кроме того, вам придется "проглотить" немного теории, касающейся объектно-ориентированного программирования. Вас также ожидает встреча с настоящей живой работающей .NET-программой.

/Звес/ениевп
Проектированием программ называется этап, на котором принимается решение о том,
что же именно программа должна делать. Для этого нужно четко определить проблему или
круг проблем, которые должны быть решены, и выработать стратегию их решения.
Написание программ — это этап, во время которого вы садитесь и набираете коды своей
программы. Обычно вы набираете инструкции языков программирования высокого уровня.
Например, одной строкой можно сказать компьютеру, что нужно вывести что-то на экран. В
этом и следующих разделах вы познакомитесь с множеством команд языка C++, с помощью
которых можно заставить компьютер делать что-нибудь полезное.
Далее программу нужно откомпилировать. На этом этапе Visual C++ .NET преобразует
коды C++ высокого уровня, понятные вам, в коды низкого уровня, понятные компьютеру. В
процессе проектирования и написания программы обычно разбиваются на несколько отдельных файлов (поскольку это удобно, а иногда и полезно). В процессе компиляции программы
эти файлы объединяются в приложение. После того как программа откомпилирована, компьютер знает, как ее нужно выполнять.
Теперь можно запустить программу на выполнение. Если она не примитивна и состоит более чем из нескольких строк, вполне вероятно наличие в ней ошибок. Поскольку ошибки не
всегда бывают явными, необходимо тщательно протестировать программу, чтобы убедиться
в том. что она работает именно так, как было задумано.
Процесс поиска и исправления ошибок называется отладкой программы. Для поиска
ошибок обычно используются услуги отладчика. Когда ошибка найдена, вы снова возвращаетесь к этапу редактирования кодов программы для ее исправления.
Все программы состоят из команд, объясняющих компьютеру, что нужно делать и как
обрабатывать имеющиеся данные. Почти все действия программ сводятся к получению, обработке и отображению (или сохранению) данных. Даже, например, программы видеоигр заняты в основном только этим. Они получают данные, следя за нажатием клавиш, движениями

Глава 2. Что такое программа

25

мыши или джойстика, обрабатывают данные, принимая решения, что нужно делать дальше, и
затем отображают результат (например, на экране отображается следующая комната лабиринта и с потолка прыгают два монстра с пушками в лапах).
Языки высокого и низкого уровня
Чтобы понять разницу между языком программирования высокого уровня и языком программирования
низкого уровня, сравните между собой выражение, набранное в C++ (язык высокого уровня}, и эквивалентный ему код, набранный на языке ассемблера (низкий уровень}.
Код C++:
а = 3*а - Ь*2 + 1;
Эквивалентный ему код ассемблера:

1

mov
lea
mov
add
sub
inc
mov

eax, DWORD PTR _a$[ebp]
eax, DWORD PTR[eax+eax*2]
ecx, DWORD PTR _b$[ebp]
ecx, ecx
eax, ecx
eax
DWORD PTR _a$[ebp], eax

В действительности коды ассемблера также относятся к более высокому уровню, чем коды, которые компьютер можетвоспринимать непосредственно (без компиляции}. Компьютер может понимать только машинный язык, представляющий любые инструкции и команды в числовом виде.
Вот как выглядит фрагмент кодов машинного языка:
8Ь 45
8d
3b
03
2d
40
89

fc
04 40
4d f8
c9
cl
45 fc

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

Тлавяал (рунщил n/toi/tauut&t
Выполнение всех программ, написанных на языке О + , начинается с функции, именуемой
main. При запуске программы прежде всего выполняется первое выражение функции main.
Выражение — это строка кодов, представляющая собой отдельную инструкцию для компьютера. (Функция состоит из группы выражений, собранных вместе для решения определенной
задачи. Более подробно функции будут рассмотрены в следующей главе.) Затем поочередно
выполняются все остальные выражения — по одному за раз.
Инструкции некоторых выражений выполняются только в тех случаях, если справедливо
какое-то условие. Такие выражения называются условными операторами. Так, строка кодов
может содержать, например, следующую информацию для компьютера: "Если пользователь
щелкнет на кнопке Печать, выведи открытый документ на печать'".

26

Часть I. Первое знакомство с Visual C++ .NET

Переменные используются для представления данных в программе. Например, если
нужно запомнить имя пользователя, можно создать переменную Имя. Затем в любой момент, когда потребуется имя пользователя, можно просто сослаться на значение переменной Имя. В процессе выполнения программы значения переменных могут изменяться. Например, можно присвоить переменной Имя значение Мартин, а потом другим выражением
присвоить этой же переменной значение Степан. Но само по себе значение переменной
никогда не меняется — в любом случае вы должны написать какое-нибудь выражение, меняющее одно значение на другое.
Комментарии используются для описания того, что происходит в процессе выполнения программы. Вы можете добавлять их для расшифровки целей, с которыми пишутся
те или иные фрагменты кодов, для фиксирования каких-то мыслей и идей, для описания
решаемых задач. Добавляя комментарии, вы упрощаете чтение кодов вашей программы
пользователями. Для вас комментарии также могут быть очень полезны. Если через некоторое время вы захотите внести изменения в уже набранные коды, вам, скорее всего,
трудно будет вспомнить, для чего используется та или иная переменная и зачем нужно
было создавать ту или иную функцию. В таких случаях, пожалуй, комментарии могут
быть единственным средством, которое поможет вам вспомнить, что же именно вы хотели сделать, набирая эти коды. (Кроме того, в комментариях программы вы можете высказать все, что наболело. Компьютеру ведь все равно, а вы избавитесь от лишнего груза.) При преобразовании кодов C++ в машинные коды компилятор игнорирует строки,
являющиеся комментариями, и просто пропускает их.
На рис. 2.1 показана небольшая программа, в которой используются функция main, выражения, переменные и комментарии.
#using
int nMylnt;

-•

int main (void)
//Получение значения от пользователя
nMylnt = GetNumberf); -*
if (nMylnt > 0) —


return nMylnt;
return -1;

Переменная
Начало функции main
Комментарий
Выражение
Условный оператор

Рис. 2.1. Основные компоненты, из которых состоит программа

CtnattqafitnMyte noqnfiotfiaMMU
Коды общих для большинства программ алгоритмов хранятся в компоненте среды
.NET, называемом CLR (Common Language Runtime). Например, почти все программы
отображают какие-то значения на экране. Возможно, это покажется вам странным, но
для выполнения такого простого действия компьютеру нужно сделать множество шагов.
Вместо того чтобы писать целые наборы кодов для всех этих шагов каждый раз, когда
нужно что-то вывести на экран, вы можете просто воспользоваться готовой подпрограммой, созданной для среды .NET.

Глава 2. Что такое программа

27

Когда вы пишете неуправляемые программы (те, которые создаются с ис) пользованием старой версии С-+), подпрограммы, реализующие типичные
-/ алгоритмы, содержатся в так называемых библиотеках. Чем они отличаются
от CLR? По сути, ничем. И тс и другие являются библиотеками. Разница
только в названии. CLR может быть использован при создании программ на
языках Visual Basic, О- и Visual С"! - (при этом сами библиотеки для разных
языков будут несколько отличаться). Хотя CLR, по существу, является тем же
набором библиотек, его возможности значительно шире возможностей стандартных библиотек языка О -t.
Г:сть два типа библиотек: статические и динамические. Если вы используете при
создании своей программы процедуры статической библиотеки, коды этих процедур непосредственно копируются в текст вашей программы, увеличивая таким
образом ее размер. Если же вы используете динамические библиотеки
(называемые также DLL), сами процедуры не копируются в вашу программу, а
вызываются в процессе ее выполнения.

н/гог/гаммы
Любая программа создается для решения каких-то задач. При этом, перед тем как начать писать колы, нужно разделить задачу на несколько отдельных логических частей и затем создать процедуры, каждая из которых будет решать проблемы своей части. Поначалу
перспектива разделения общей задачи на несколько более мелких может показаться довольно сложной, однако по мере приобретения опыта программирования вы научитесь делать по быстро и почти автоматически. (Любые хорошие компьютерные курсы уделяют
лому вопросу большое внимание.)
В качестве примера рассмотрим вариант создания программы для решения обычной задачи. Предположим, вам нужно вычислить площадь квадрата. (Зачем? Какая разница. Может, у
вас вечеринка и вы хотите чем-то удивить своих друзей.) Вше со школы вы помните, что
площадь квадрата вычисляется как умножение его стороны на саму себя.
В контексте создания программы эта задача может быть разбита на три логические
подзадачи.
1.
2.
3.

Определение длины стороны квадрата.
Вычисление площади квадрата путем умножения стороны на саму себя.
Отображение полученного результата.

Теперь, когда общая проблема разбита на отдельные подзадачи, каждую из них следует
преобразовать в коды программы, чтобы компьютер знал, какие задачи ему нужно решать.
Например, на языке C++- .NET эти коды могут выглядеть так:
/ , 3^".^ г.сг. '.'оа
/ /'Ъы'-глг.лен.'ле площади квадрата при условии, что известна
/ ' }\:iVM7i

О~С

СТОрОНЫ

vr: :"!"i-jde "s tdaf x . h"

#.:sinc

28



Часть I. Первое знакомство с Visual C++ .NET

using namespace System;
//С этой строки начинается выполнение программы
tifdef _UNICODE
int wmain(void)
ttelse
int main{void)
#endif
String *pszSize;
int nSize;
int nArea;
//Запрос от пользователя значения длины стороны квадрата
Console::WriteLine(Ь"Чему равна длина стороны квадрата?"]
//Получение ответа
DszSiire = Console :: ReadLine
//Преобразование полученного значения в число
nSize = nSize.Parse(pszSize);
//Вычисление площади квадрата
nArea = nSize*nSize;
//Отображение результата
//Обратите внимание, что для этого вначале нужно
//преобразовать полученное значение в строку
Conso]е::WriteLine(L"Площадь квадрата составляет {0}
единиц.", nArea.ToString()};
//Ожидание, пока пользователь не остановит
//выпслнение программы
Console: :WriteLine (L/'Нажмите Enter, чтобь: завершить
выполнение программы");
Console::ReadLine();
return 0;
}
Итак, вы только что узнали, как выглядят коды программы. Но как разобраться, для чего
нужна каждая отдельная строка? Для этого сначала познакомьтесь с общими принципами
чтения кодов программы (если вы их еще не знаете).
1.

Начинайте читать с самого начала.

2.

Читайте по одной строке.

3.

Старайтесь понять, для чего нужна каждая строка.

4.

Если вы чего-то не понимаете, переходите к следующей строке.

Глава 2. Что такое программа

29

Эти же принципы используются при работе компилятора, за исключением разве что четвертого пункта.
Далее описывается, как вы могли бы интерпретировать коды приведенной выше программы. Итак, обратимся к первым двум строкам:

//SquareArea
//Вычисление плошади квадрата при условии, что известна
//длина его стороны
Увидев их, вы можете подумать: "Ну, в этих строках нет ничего сложного. В них говорится, что эта программа вычисляет площадь квадрата исходя из длины его стороны". (Две косые черты в начале строк указывают, что эти строки являются комментариями.)
#include
#using
using

"stdafx.h"


namespace

System;

"А это уже что-то совсем непонятное. Наверное, о том, для чего все
это нужно, я узнаю в следующих г л а э а х " . (Совершенно правильный ход
мыслей.)
# i f d e f ^UNICODE
i n t wmain(void)
ttelse
i n t main(void)
#endif
''Опять ЧТО-ТО непонятное. Пожалуй, сейчас это можно пропустить".
String *pszSize;
i n t nSize;
i n t nArea;
"Зачем нужны зти строки я точно не знаю, однако
и area (площадь) уже как-то созвучны с решаемой
//Запрос от пользователя значения длины стороны
C o n s o l e : : W r i t e L i n e ( Ь " Ч е ы у равна длина стороны

слова s i z e (длина)
задачей".
квадрата
квадрата?"!;

//Получение ответа
pszSize = Console::ReadLine(};
//Преобразование полученного значения в число
nSize = n S i z e . P a r s e ( p s z S i z e ) ;
'"Все это выглядит немного странно, но в целом можно понять, что коды эти нужны для
того, чтобы компьютер узнал, чему равна длина стороны квадрата".
// Вычисление площади квадрата
nArea = r . S i z e * n S i z e ;
"Да, да! Это я понимаю! В этой строке длина стороны умножается на саму себя, и мы находим площадь квадрата"'.
Ну и так далее.

30

Часть I, Первое знакомство с Visual C++ .NET

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

inenefib немного fneoftuu
Вы только что узнали, как выглядят коды настоящей программы на С+-=-. Однако в этой
программе не были использованы возможности объектно-ориентированного программирования. Язык C++ настолько популярен во многом благодаря именно этим возможностям. Почему? На это есть ряд причин.
I S Объектно-ориентированное программирование позволяет повторно использовать
I
одни и те же коды, что значительно экономит время и усилия, необходимые при
\
создании программы.
;• ^ Объектно-ориентированные программы имеют хорошую структуру, что сущест:.
венно облегчает чтение и понимание их кодов.
I S Объектно-ориентированные программы легко тестировать. Их можно разбить на
% небольшие компоненты и отдельно проверять работу каждого из них.
;• ^ В объектно-ориентированные программы очень легко вносить изменения и добавi
лять новые возможности.
Основная идея объектно-ориентированного программирования проста. Данные и процедуры, обрабатывающие эти данные, объединяются в одно целое, называемое классом.
Если нужно получить доступ к данным какого-то класса, используются процедуры этого
же класса.
В старых процедурных языках программирования, напротив, данные и процедуры, их
обрабатывающие, не могли быть объединены и воспринимались как совершенно разные
компоненты.

Что такое объект и с чем его едят
В основу всего объектно-ориентированного программирования положена такая идея:
разбить решаемую задачу на группу объектов. Объекты состоят из данных и процедур,
которые эти данные обрабатывают. Например, рассмотрим программу, управляющую
дисководом с автоматической сменой дисков. Она может состоять из нескольких объектов, обрабатывающих информацию о воспроизводимых с устройства музыкальных треках. Один объект, например, может использоваться для представления воспроизводимых
треков, а второй для представления самого дисковода. Каждый объект должен содержать данные, которые его описывают (объект Трек может содержать информацию о
продолжительности песни), и функции, которые оперируют этими данными (объект
Трек может иметь функцию, отображающую название песни и ее продолжительность).
Объединение данных и обрабатывающих их функций в отдельные объекты называется
инкапсуляцией.
Уже готовые объекты можно объединять между собой для создания новых объектов. Это
называется композицией. Например, можно создать объект Караоке, объединив объект
ДИСКОБОЛ С объектом Микрофон. (Можно создать объект Сутки, объединив объект День с
объектом Ночь.)

Глава 2. Что такое программа

31

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

\ 1е нужно повторно набирать те же коды: все коды, которые набраны для исходных
объектов, автоматически могут быть использованы новыми объектами.

*•" Снижается вероятность возникновения ошибок: если вы точно знаете, что исходный объект работал правильно, значит, любые возникшие ошибки следует
искать в кодах, которые были добавлены к новому объекту при его создании. С
другой стороны, если вы найдете и исправите ошибку в исходном объекте, она
автоматически будет исправлена для всех других объектов, созданных из данного путем наследования.
•S Коды программы становятся легче для чтения и понимания: нужно понять, как работают исходные объекты. Понять, как работают объекты, созданные путем наследования, будет намного проще, поскольку вам останется только изучить, как работают добавленные к ним данные и функции.
Еще одним свойством объектно-ориентированного программирования является тот факт,
что одна и та же функция может выполняться по-разному в зависимости от того, данные какого объекта обрабатываются. Это свойство называется полиморфизмом. Например, если
функция должна распечатать содержимое ячейки электронной таблицы, на печать выводится
число. Вели же функция должна распечатать диаграмму, на печать выводится изображение. В
обоих случаях используется одна и та же ф\нкция, но поскольку объекты разные (ячейка и
диаграмма), выполняемые действия б\дуг совершенно различными.

Инкапсуляция
Инкапсуляцией называется выделение данных и функций, обрабатывающих эти данные, в
отдельные 'элементы, называемые объектами или классами. Данные, в зависимости от того
как они используются, иногда называют свойствами классов. Функции также иногда называют методами классов.
Секрет создания качественной объектно-ориентированной программы заключается в том,
чтобы выделить классы, которые бы максимально точно описывали реальную решаемую
проблему и которые можно было бы максимально часто повторно использовать. Поначалу
эта задача может показаться довольно сложной, однако по мере приобретения практического
опыта вы научитесь это делать почти автоматически.

Наследование
Ото одно из наиболее интересных свойств объектно-ориентированного программирования. Как отмечалось ранее, с помощью этого свойства можно создавать новые объекты, используя для этого уже существующие. Новый класс, созданные на основе готового класса, называется производным. А тот класс, который был взят за основу, называется базовым. (Иногда производные классы называются дочерними, а базовые —
родительскими.')

32

Часть!. Первое знакомство с Visual C++ .NET

На рис. 2.2 показано семь классов. Класс Звук является самым простым. Он содержит
название воспроизводимого звука и его продолжительность. Класс Трек основан на классе
Звук, а потому также содержит информацию о названии и продолжительности. Кроме того, он содержит данные об исполнителе и о дате записи. Класс Rock является производным от класса Трек. Он содержит все элементы класса трек (как и все элементы класса
Звук) плюс еще некоторые. Таким образом, на основе любых классов можно создавать
более сложные классы, содержащие в себе больше разных данных и функций для их обработки. При этом каждый производный класс содержат в себе все данные и функции, которые есть в базовом классе.

Звук
Название
Продолжительность

7

ЗвукиЖивотных
Животное

Rock
Группа
Альбом

\

Трек

Исполнитель
ДатаЗаписи

t
Классика
Дирижер
Оркестр
ПерваяСкрипка

Pop
Альбом
Рейтинг

1
Alternative Rock
Направление

Рис. 2.2. Новые классы можно создавать путем наследования свойств и методов уже созданных классов

Полиморфизм
При создании новых классов можно использовать не только возможность наследования, но также полиморфизм для определения способа поведения новых классов. Если вы
еще не запомнили все греческие слова, употребляемые в этой главе, напомним, что под
полиморфизмом подразумевается возможность одних и тех же функций по-разному обрабатывать данные, принадлежащие разным объектам. Например, класс Звук может
иметь функцию Получить. Если запустить ее для объекта ЗвукиЖивотных, она может
отобразить на экране сообщение, советующее взять с собой диктофон и сходить в ближайший зоопарк. Если запустить эту же функцию для объекта Rock, может быть предпринята попытка открыть соответствующую Web-страницу с возможностью загрузки
нужного файла.

Глава 2. Что такое программа

33

Используя одновременно возможность наследования и полиморфизм, вы сможете легко
создавать наборы подобных, но уникальных объектов. Благодаря наследованию объекты будут во многом похожи друг на друга. А благодаря полиморфизму каждый из них может чемю оыичатьея от других. Так, если вы используете полиморфизм, то функция, прнсутствую1И
. а я в разных объектах (такая, как функция Получить), для каждого из них будет работать
по-разному.
Ьслч для какой-то функции полиморфизм не используется, для производного класса она
будет выполняться точно так же, как и для базового. Если для класса, производного от класса
;
•- •:'-:. не перегрузить функцию " с л у ч и т ь (т.е. просто наследовать ее без внесения изменении), то ее выполнение также будет приводить к попытке открыть ту же Web-страницу. Таким образом, производные классы изначально имеют ту же функциональность, что и базовые.
h e m же какие-то функции отличаются, значит, к ним был применен полиморфизм.

34

Часть I. Первое знакомство с Visual C++ .NET

Глава3

Приступаем к созданию программ
В э/Оой главе...
> Что такое исходные файлы
> Создание и выполнение управляемых программ .NET C++
> Создание и выполнение неуправляемых программ C++

о

+у та глава поможет вам создать свою первую программу C++. (И даже не одну.) Нач^ ^ нем с создания управляемой программы C++. Это дружественное определение дано
разработчиками среды .NET программам, создаваемым для работы в этой среде, и обозначает, что при разработке этих программ предоставляется множество дополнительных возможностей (благодаря CLR) и обеспечивается их корректное выполнение (благодаря правильному
управлению памятью и многому другому). Также вы научитесь создавать добрые старые (или
стандартные) программы C++, для которых разработчики среды .NET придумали нехорошее
название— неуправляемые программы. Но вначале вы научитесь создавать то, что является
основой любой программы C++ — исходные файлы.

Зачем нужны исходные файлы
Исходные файлы, или файлы реализации с расширением СРР (читается как С Plus Plus)
либо СХХ, содержат в себе основную часть выполняемой программы. Именно здесь определяются функции, данные и ход выполнения программы. (Под ходом выполнения программы
подразумевается последовательность, в которой будут выполняться отдельные выражения
или части программы.)
Исходные файлы состоят из выражений. Выражением является строка программы, которая дает указание компьютеру выполнить какое-то действие.
Например, приведенная ниже строка является выражением, которое указывает компьютеру,
что необходимо вычислить площадь квадрата путем умножения длины стороны на саму себя:
nArea = n S i z e * n S i z e ;
Каждое выражение должно заканчиваться точкой с запятой. Точка с запятой показывает компьютеру, где заканчивается одно выражение и начинается другое
(так же, как на письме одно предложение заканчивается точкой, после чего начинается другое предложение).
Можно объединить несколько выражений, заключив их в фигурные скобки ( { } ). Например, допустим, что есть такое выражение:
i f чай горячий, подожди немного;
(Команда if, которая будет более подробно рассмотрена в главе 11, указывает компьютеру на необходимость выполнить какое-то действие в случае, если справедливо некоторое условие.) Если вы хотите, чтобы в это же время компьютер сделал еше какое-то действие, можно набрать такое выражение:
i f чай горячий {подожди немного; просмотри почту;}

Глава 3. Приступаем к созданию программ

35

Два выражения были объединены с помощью фигурных скобок.
Открывающую и закрывающую фигурные скобки можно также использовать для обозначения начала и окончания функции. Более подробно об этом речь идет в главе \2.
Если для выполнения функции требуется указание параметров, они заключаются в круглые скобки. Например:
i f номер з а н я т {немного п о д о ж д и ( 2 м и н у т ь : ) ; п о з з о н и еще р а з ; }
Таким образом функция подожди точно указывает, сколько нужно подождать, перед тем
как еще раз позвонить. Узнать об этом более подробно (не о телефонных звонках, а об использовании параметров), вы сможете, прочитав главу 12.
Итак, подытожим полученные знания об использовании выражений:
; S строки заканчиваются точкой с запятой (;);
1 S

объединение строк осущест вляется с помощью фигурных скобок ({ });

\ S

значения параметров заключаются в круглые скобки — ( ) .

все s0io

на nfiatctnuice

Пожалуй, теории пока достаточно. Самое время заняться делом. Начнем с создания нового проекта .NET.
1.

Выберите команду File^New^Project (Файл^Создать^Проект).

2.

Откроется диалоговое окно New Project.

3.

В разделе Project Types (Тип проекта) выберите пункт Visual C++ Project.

4.

В разделе Templates (Шаблоны) выберите Managed C++ Application.

5.

В поле Name (Имя) наберите H e l l o World.

6.

В папке Visual Studio Projects (она расположена в папке My Documents) будет
создана папка Hello World.
Обратите внимание на рис. 3.1. Если вы хотите создать папку Hello World в какойнибудь другой папке, укажите ее название в поле Location (Размещение) или щелкните
на кнопке Browse (Обзор), чтобы открыть окно для поиска этой папки.

Hew Project
pree
l ct Types:
_J VsiualC* Proj-ts
KualC++ Proe
j ct!
:1 _j V
_J Seu
to and DepolymentProects
i + _| «ret PrDe
jftS
! _J VsiualStudo
i Sou
lo
tins

Ш
i i! SJ
Prcnie^ur Dll

' ManaaedC+-V
i Appkatian

Managed :++
try

M
an
Em
paytgedProCft+
c+
t

An appcilato
i n thatau«M ^ E -ten»ms fen Ci +.
faj me:
- Hdo
lA
'o
'rW
i С >p«unents arid Se'tings^Bdrriian My Docurceri •№
Locator:
•*\ &owse., }
jkjse Solution
&dd to ^oJuo
tin
o
tfejct wB
i be createdссл.-ЛВаг son'iMv Documents\Vimal Studio ProiecB\HeloW»ld.
ncef
i

I
- ^ J
Рис. З.1. Использование диалогового окна New Project
для создания проекта Hello World
36

Часть t. Первое знакомство с Visual C++ .NET

7.

Щелкните на кнопке ОК.
Visual C++ создаст целый набор файлов, принадлежащих одному проекту. Один или
несколько создаваемых проектов составляют одну задачу {solution). Более подробно о
задачах речь идет в главе 4. Окно Solutin Explorer (Разработчик задач), показанное на
рис. 3.2, отображает файлы, из которых состоит проект Hello World.
Solution Explorer - HelbWorld
5olution 'HelloWorid' (l project)
VbrUj
•~ j Source Files
j£] HelloWorid.cpp
i j Assemblylnfo.cpp
\£\ stdafx.cpp
C^ Header Files
У stdafx.h
?"""! Resource Files
У ReadMe.txt

i!3 Solution Explorer \Щ
Рис. З.2. S окне Solution Explorer
можно увидеть файлы, используемые
данным проектом

Все эти файлы были созданы для того, чтобы можно было реализовать простую программу, отображающую на экране сообщение H e l l o World. Чтобы увидеть исходные коды программы, дважды щелкните в окне Solution Explorer на значке HelloWorid. cpp. Этот файл
расположен в папке Source Files (Исходные файлы) проекта Hello World.
К счастью, эта простая программа, отображающая на экране сообщение H e l l o World,
может быть создана автоматически. Для этого выполните следующее.
1.

2.

Выберите команду Debugs Start Without Debugging (Отладка'ФЗапустить без
отладки).
Visual C++ отобразит сообщение о том, что проект HelloWorid - Debug Win32 является устаревшей программой.
Щелкните на кнопке Yes, чтобы Visual C++ создал этот проект автоматически.
Откроется окно Output (Выходное окно), в котором вы сможете наблюдать за процессом построения проекта.
Как только проект будет создан, на экране мелькнет черное окно и сразу же исчезнет.
Разумеется, вы не успеете заметить, что было в этом окне. Так случается всегда, когда
программа просто выполняет набор каких-то действий и сразу завершает свою работу.

Но как же определить, правильно программа работает или нет? И делает ли она вообще
что-то? Сделать это можно разными способами. Из материала главы 16 вы узнаете, как можно использовать отладчик для остановки в самом конце выполнения программы. А далее в
этой главе описано, какие коды необходимо добавить, чтобы программа в нужном месте остановилась и ожидала, пока пользователь не даст ей сигнал продолжать работу. По мере приобретения опыта программирования вы оцените полезность и универсальность этого приема.
Ну а пока выполните следующее.
1.

Откройте окно для ввода команд.

Глава 3. Приступаем к созданию программ

37

2.

Если у вас установлена Windows 2000 или Windows XP, выберите команду
S t a r t ^ P r o g r a m s ^ A c c e s s o r i e s ^ C o m m a n d Prompt. Если у вас установлена Windows NT, выберите команлу S t a r t s P r o g r a m s ^ C o m m a n d Prompt.
Используйте команлу CD, чтобы перейти в папку с проектом HelloYVorld.
Если имя папки содержит пробелы, возьмите его в кавычки. Для Windows 2000 и
Windows XP путь к папке может выглядеть приблизительно так: "C:\Documeius and
.Seltins£s\MyUserNaine\My DocumentsWisual Studio ProjeclsVHclloWorld". (В действительности путь пуде'1 зависеть от настроек вашего компьютера. Например, вместо
слова MyUserName нужно будет указать ваше пользовательское имя.)
Чтобы запустить программу, наберите D e b u g \ H e l l o W o r l d .
Поскольку' Visual Studio создала проект автоматически, т.е. со всеми установками, заданными по умолчанию, она поместила файл, с которого начинается выполнение программы.
в папку Debug. Результат выполнения программы вы можете увидеть на рис. 3.3.

Ш Command Prompt
:\Docunents and SettingsNBarnsonSMy DoeumentsMJisual Studio Projects\HellolJorld
M « bf f le l l d
lello World
,'i\I>ocuinents And SettingsNBarnsonNMy DocunentsMJisual Studio PrajectsSHelloUor

Рис. 3,3. В окне для ввода команд отображается надпись Не 11 о Worl a

С чего начинается выполнение программы
Теперь, когда вы видели результат выполнения программы, давайте посмотрим, каким
оорачоч все это происходит. Для начала компьютер должен определить, с какой строки нужно начинать выполнение программы. Вместо того чтобы нумеровать все строки (как это делалось в старые добрые времена), нужно просто поместить первую строку программы в
функцию, именуемую m a i n . С первой строки .пой функции начинается выполнение любой
программы. Например, вот код простой программы, которая ничего не делает:
дг;

nic-iin? v o i d )

.' ' '_;;;ось а б с о л ю т н о н и ч е г о не п р о и с х о д и т
Выполнение этой программы, как и всех остальных, начинается с функции m a i n . Поско.1ьк> функция m a i n (как и любая другая функция) может использовать в своей работе значения параметров, после ее названия следует пара круглых скобок. (В среде .NET функции
обычно используются без каких-либо параметров, поэтому вместо них постоянно набирают
сюоо vMia. обозначающее факт их отсутствия.) Далее следует открываюшая фигурная скоб!..л. которая указывает компьютеру, что за ней идут строки функции m a i n . Закрывающая фиг
\ рнля скобка говорит о том, что на этом выполнение функции заканчивается. Вам может пока^п;.ся непонятным значение слова : n t , стоящего перед названием функции. Более подробно это рассматривается в главе 12, а пока напомним, что функция— это отдельная
подпрограмма, которая может выполнять какие-то действия {например, выводить что-то на
печать), обрабатывать данные или запрашивать какую-либо информацию. Функции могут
!а].же возвращать в качестве результата какое-то значение. В С~+ mair. также является
фу.чмшей. а следовательно, может возвращать какое-нибудь число (integer).

Часть I, Первое знакомство с Visual C++ .NET

Начало функций main всех управляемых программ C++, коды которых вы встретите в этой книге, выглядит несколько сложнее:
# i f d e f JJNICODE
i n t wmain(void)
#else
i n t main(void)
#er:dif
Слово _UNICODE обозначает, что будет использоваться стандарт, предусматривающий ввод с клавиатуры букв и символов других языков, включая азиатские.
При компиляции программы может быть выбрана опция, указывающая на то, что
программа должна быть откомпилирована с учетом этой возможности.

Как организовать диалог с пользователем
Конечно, можно создавать программы, которые ничего не будут делать. Но все же лучше,
если программа будет в состоянии отобразить что-либо на экране и принять какую-нибудь
информацию от пользователя. Есть миллион способов (ну хорошо, пять или чуть больше)
сделать это в C++ .NET. Самый простой— использовать для этого функции Console. Они
позволяют считывать и отображать сообщения в окне консоли (это то черное окно, которое
вы видели при выполнении вашей первой программы). Чтобы отобразить сообщение на экране, используйте команду Console : : WriteLine. Дайте ей любой текст, и она высветит его
в окне консоли. Например, показанная ниже строка выводит на экран сообщение H e l l o
World 'обратите внимание, что текст взят в кавычки):
C o n s o l e : : W r i t e L i n e ( " H e l l o World") ;
Допустим, вместо этого сообщения нужно отобразить текст Здравствуйте,
тетя ! Делается это аналогичным образом:
Console::WriteLine("Здравствуйте,

я вл^а

я ваша т е т я ! " ) ;

Чтобы получить информацию от пользователя, используйте команду C o n s o l e : :
ReadLir.e. Полученная информация будет воспринята как строка текста. (В главе 7 рассматривается, как преобразовать эту информацию к какому-нибудь полезному виду.) Например,
приведенная ниже строка заставит компьютер ждать, пока пользователь не введет какомнибудь текст:
Console::ReadLine();
Обратите внимание, что команда C o n s o l e : :ReadLir.e ничего не отображает
на экране. Обычно для получения от пользователя каких-либо данных вначале
нужно использовать команду C o n s o l e : : W r i t e L i n e , чтобы отобразить на экране соответствующий
вопрос, а затем
уже
применить
команду
Console : : ReadLine, чтобы принять от пользователя ответ. Например:
Console::WriteLine(L"Нажмите E n t e r , чтобы завершить
выполнение программы");
Console::ReadLine();
Функции Console являются частью .NET библиотек CLR, Поэтому, перед тем как их мспользов;у"Ь, нужно указать Visual C++ .NET, в каком месте их искать. Сделать э'ю можно с
помощью ключевого слова f u s i n g . Все функции CLR разбиты на множество динамических
библиотек (файлы с расширением DLL). Строки со словом #usir.g сообщают компьютеру,
функции каких именно библиотек будут использованы в данной программе.

Глава 3. Приступаем к созданию программ

39

CLR является аОбревиатурой от Common Language Runtime (переводится приблизительно
как общий язык выполнения программ), но, как вы уже знаете, представляет собой нечто
иное, а именно совокупность библиотек с различными полезными функциями. Название
Common Language Runtime было выбрано исходя только лишь из маркетинговых соображений. Кстати, сокращенное название CLR впервые упоминается только в этой книге, так что,
если вы произнесете его в разговоре со своими друзями-программистами и они не поймут, о
чем речь, просто посмотрите на них свысока и скажите, что они отстали от жизни.
Большинство основных функций .NET, включая функции Console, расположены в библиотеке m s c o r l i b . Ее название происходит от слов Microsoft core library, что переводится как
''корневая библиотека". Возможно, это поможет вам запомнить ее название. Если в своей программе вы захотите использовать функции библиотеки mscorlib.dll, наберите такую строку:
"using
Все функции CLR разбиты на отдельные группы, обозначаемые термином manespace
(пространство имен). Эти группы состоят из классов (более подробно классы описаны в главе 17). А классы, в свою очередь, содержат функции. Например, функции ReadLine и
W r i t e L i n e принадлежат классу Console. Класс Console относится к группе System. Таким образом, полное имя функции ReadLine будет выглядеть так: System: : C o n s o l e
::ReadLine.
Ключевые слова u s i n g manespace могут избавить вас от необходимости каждый раз
при вызове функции набирать название ее группы. Например, в кодах программы HelloWorld
вы могли видеть такую строку:
u s i n g namespace System;
Именно благодаря ей далее по тексту программы можно набирать Console : : ReadLine
вместо System: : Console : : ReadLine.
Обратите внимание, что ключевые слова #using и u s i n g namespace не являются частью стандартного языка С+-г. Точно так же, как и все функции CLR. Поэтому все команды, с которыми вы познакомились до этого, могут быть использованы только при создании программ .NET. Далее в главе вы узнаете, как написать аналогичные программы, используя возможности стандартного языка C++.

Не скупитесь на комментарии
Только вы можете точно знать, для чего вами была набрана та или иная команда. Но если
коды вашей программы должен будет смотреть еще кто-то, он этого, скорее всего, не поймет.
Или, если спустя несколько лет вы захотите внести в программу некоторые изменения, вы
уже вряд ли сможете вспомнить, как это все работает. Вот почему так важно использовать
комментарии. Комментарии объясняют простым русским языком (или не русским, но обязательно тем, на котором вы привыкли разговаривать), что вы пытались сделать, набирая все
эти коды.
В кодах программ, приводимых в этой книге, вы уже сталкивались с комментариями. Распознать их помогут две косые черты (//). Как вы уже могли догадаться, весь текст строки,
следующий за ними, является комментарием. (Прочитайте раздел "Старый формат комментариев", чтобы знать, как еще можно создавать комментарии.)
Комментарий может быть выделен в отдельную строку:
//Это строка комментариев.
Также комментарий может завершать собой какую-то строку:
э = 10; //Присвоение переменной значения 10.

40

Часть I. Первое знакомство с Visual C++. NET

Старый формат комментариев
В языке C++ для обозначения комментариев используются две косые черты (//). Ранее в языке С комментарии выделялись несколько иначе; начало комментария обозначалось символами / *, окончание * /. Этот же способ по-прежнему можно использовать в языке C++. Например:
/* Это старый способ выделения комментариев */
а = 10; /*Присвоение переменной значения 10*/
/^Переменной а*/ а = 10; /"^присваивается значение 10*/
При использовании старых комментариев самое главное - не забыть обозначить их окончание! Если вы
забудете набрать в конце комментария символы */, компилятор проигнорирует все, что было набрано
после открытия комментария символами /*.
Обратите внимание, что, когда для создания комментария используются две кхые черты (//), никакими
специальными символами заканчивать строку не нужно. Две косые черты говорят компилятору, что далее
вся строка (и только эта строка) является комментарием. Однако, в отличие от старого формата, если
комментарий занимает несколько строк, каждую новую строку должны предварять две кхые черты {//).

Исходный код программы HelloWorld
Теперь, когда вы уже достаточно знаете о принципах создания программ Visual C++ .NET,
посмотрите на исходный код созданной ранее в этой главе программы HelloWorld. Он должен быть вам понятен (в нем действительно нет ничего сложного).
//Это код исходного файла приложения VC+ +
//Программа была создана с использованием мастера
#include

"stdafx.h"

#using < m s c o r l i b . d l l >
u s i n g manespace System;
//С этой строки начинается выполнение программы
#ifdef ^UNICODE
i n t wmain(void)
#else
i n t main(void)
#endif
{
C o n s o l e : : W r i t e L i n e ( " H e l l o World");
Return 0;
}
Как видите, программа начинается с комментариев. Далее следуют строки с ключевыми
словами # u s i n g и u s i n g namespace, определяющие способ использования CLR. Затем,
после еше одного комментария, следуют коды функции main, с которых, собственно, и начинается выполнение программы. На экране будет отображена надпись H e l l o World, сразу
после чего выполнение программы прекратится. Строка r e t u r n 0 говорит о том, что функция main возвращает нулевое значение. Вы можете пока не обращать на нее внимания.

Глава 3. Приступаем к созданию программ

41

Строки бывают разными
В компьютерной терминологии текстовое значение, такое как "Hello World", называется строкой (string).
При использовании строк перед ними можно добавлять специальные буквы, предоставляющие компьютеру дополнительную информацию. Например, если строка должна быть интерпретирована как набор
международных символов {Unicode), наберите перед ней букву L;
Console::WriteLine(L"Hello World");
Строки Unicode могут содержать в себе любые международные символы, включая символы японского
алфавита. Однако в языках .NET, если вы не укажете, что строка содержит символы других алфавитов,
она все равно будет преобразована к строке Unicode в процессе выполнения программы. Поэтому самостоятельно указывать, что строка является строкой Unicode, особого смысла не имеет.
Намного разумнее набрать перед текстовым значением букву s:
Console::WriteLine(S"Hellc

World");

Sio будет сигналом компьютеру использовать встроенный класс string, что предоставит вам дополнительные возможности по обработке этих данных.

Пару штрихов к программе Hello World
Теперь добавим некоторые изменения к программе HelloWorld. Для начала обозначьте все
строки как строки .NET (поставьте перед ними буквы S). Затем добавьте в конце программы
несколько команд, которые позволят выполнять ее, находясь в среде разработки Visual О \Г Г : IDKi. и видеть результат выполнения до того, как окно исчезнет с экрана. Итак, оконfi код программы должен выглядеть следующим образом:
: W r i t e L i n e (S " H e l l o Werlei" ) ;
!>:e , пока пользователь не решит
ж.: :'•.- выполнение прогр^ммь:
: vVui'.GLine (5"На.кмлте Entzer, чтобы завершить выгт'"'л но -:ио
программы") ;
: RoadLine ( ) ;

о J/SJ
Все програм\ш. которые были рассмотрены ранее, являлись программами .NET.
Это означает, что они не будут работать вне среды .NET. Напротив. С.--+ является
стандартизированным языком программирования, и созданные с его помощью
программы могут работать в различных компьютерных системах. Далее аы узнаете, как создать обычную программу С+->- для отображения на экране надписи
"Hello World". Напомним, что разработчики .NET назвали программы, созданные
без использования возможностей .NET (к числу которых относятся и обычные
программы C++), неуправляемыми программами.
Однако, перед тем как приступить к программированию, познакомьтесь еще о некоторыми тонкостями терминологии. В среде /NET доступ к встроенным функциям обеспечивает
CLR- В обычном C++ функции расположены в различных библиотеках. В .NET нужно использовать ключевое слово frusing. чтобы получить доступ к функциям CLR. В C++ для использования других функций нужно подключить заголовочный фай:1 (header file). Заголовочным файл сообщит компьютеру названия и характеристики подпрограмм из других файлов
и. in библиотек, которые вы намериваетесь использовать. Например, предположим, что вы хо-

42

Часть I. Первое знакомство с Visual C++ .NET

тите использовать функцию f сю, хранящуюся в библиотеке. Для этого необходимо подключить заголовочный файл, который сообщит компьютеру, что f o o — это подпрограмма, и
укажет, данные каких типов потребуются для ее выполнения.
Если вы создаете подпрограмму в одном файле и хотите использовать ее затем в другом
файле, нужно создать заголовочный файл, описывающий эту подпрограмму. Подключая этот
файл к другим файлам, вы можете повторно использовать созданную подпрограмму в других
программах.
Для чего нужен компоновщик
Когда вы определяете внешнюю функцию в заголовочном файле, вы указываете только ее название и параметры, но не название самой библиотеки или исходного файла, в которых она может храниться. Компилятор
генерирует список всех используемых программой функций, куда входят названия как внешних функций, так
и функций, созданных в самой программе. После компилятора в действие вступает компоновщик (linker).
Помимо прочего, компоновщик проверяет список названий всех требующихся для выполнения программы
функций и ищет сами функции в других файлах и библиотеках. Если функция найдена, она используется автоматически. Если компоновщик не может найти функцию, он выдает сообщение об ошибке.

Отображение информации на экране
Если вы хотите сообщить какую-то информацию пользователю, ее нужно отобразить на
экране монитора. Работая с .NET, мы использовали для этого
команду
C o n s o l e : :WriteLir.e. В неуправляемых программах вместо нее используется выражение
c o u t (читается как see-out— "вижу-вывожу"). Если вы даете выражению cout какоенибудь значение, оно с радостью выводит его на экран. Чтобы передать выражению cout
значение, используется команда Получение контекстно-зависимой справки
> Использование строки перехода
>

Сокрытие отдельных фрагментов кодов

>

Поиск нужного текста

Ж0* олыпую часть процесса разраоотки программ!.i занимав! написание кодов в окне ре*-^ дактора. Как хороший текстовый редактор кардинально упрошает процесс написания книги (ну хорошо, не кардинально, но все же упрощает), так и лпрошии редактор кодов
значительно облегчает процесс создания программ. Рс^икптр >••)(; позволяет решать не
только обычные задачи редактирования, такие как копирование, вырешше и вставка текста,
но и специальные задачи, касающиеся создания программ.
Visual С+-г имеет многофункциональный настраиваемый релш-лор кодов, позволяющий
решать многие специальные задачи, например такие, как одновременный сдвиг нескольких
строк или быстрое подключение заюловочного файла. В этой главе дается краткий обзор
наиболее важных возможностей этого редактора.

Afoq&t а/щеапвцимп qua tnoio,
их fteqataiiufio^atnb
Visual C++ позволяет открывать для редактирования любое количество файлов (можег и не
любое, но достаточно большое). Если вы открыли cpajy несколько файлов, коды каждого из них
будут отображаться в отдельном окне. Visual C-+ поддерживает как многооконный интерфейс
MD1 (Multiple Document Interface), так и отображение окон а аиле вкшдок. IMDI — ТУ о всего
лишь интерфейс, позволяющий отображать на экране одновременно несколько окон и перемещать их независимо друг от друга.) Режим отображенияокон в виде вкладок установлен по
умолчанию. Как обычно выглядит окно редактора кодов, показано на рис. 5.1.
Для открытия окна редактора кодов обычно используют один из перечисленных ниже
способов.
^
\
%

Открытие для редактирования одного из файлов проекта. Дважды щелкните
на названии файла в окне Solution Explorer. Коды л о г о файла б\д\т отображены в
окне редактора.

Глава 5. Хороший редактор — что еще нужно бывалому программисту?

55

/ Редактирование элемента класса. Дважды щелкните на названии элемента в списке Class. Файл будет открыт в редакторе кодов, при этом курсор будет помешен
перед выбранным элементом. Можно также щелкнуть на названии элемента правой
кнопкой мыши и выбрать команду Go to Definition (Перейти к определению), чтобы открыть для редактирования коды исходного файла (с расширением .срр), или
выбрать команду Go to Declaration (Перейти к объявлению), чтобы открыть для
редактирования коды заголовочного файла (с расширением .h).
S Создание нового файла. Выберите команду File^New^File (ФайлОСоздать^
Файл), чтобы открыть диалоговое окно New- File. Выберите категорию создаваемого файла, его тип и щелкните на кнопке ОК.
V Открытие существующего файла. Выберите команду File^Open^File. В открывшемся диалоговом окне укажите название файла, который вы хотите открыть
для редактирования.
Ы^г Годе

^__
f

HelloWorltfc.cpp

i

— q пг~.

_

..--.—*

Е

i

X

:

" / / H e l l o W o r l d 2
/ /
/,/

h e l l o

w o r l d

o n

t h e

s c r e e n

U n n i d n d g e d

# i n c i u d e

" s t d a f x . h "

^ i n c l u d e

< i o s t r e a m . h >

i n t
i

P r i n t s

_ t m a i n ( i n t

a r g c j

_ T C H A R *

a r g v [ ] )

X
/ / W r i t e
c o u t

t o

< <

r e t u r n

t h e

" H e l l o

s c r e e n
W o r l d \ n " ;

0 ;

!

Рис. 5.1. Редактор используется для отображения, ввода и редактирования кодов программы
Сохранить отредактированный файл также очень просто. Можно даже сохранить сразу
все файлы, открытые для редактирования. Вот как это делается.
I V Сохранение файла. Выберите команду FileVSave НмяФапла.
I S Сохранение файла с присвоением ему нового имени. Выберите команду
1
File\Save ИмяФаша As. В диалоговом окне наберите новое имя файла и щелкните
|
на кнопке Save (Сохранить).
\ •/ Сохранение всех файлов, в которые были внесены изменения. Выберите ко••
манду File^Save All. Рекомендуется это делать перед тем, как запускать на вы•}.
полнение только что созданную программу. Если вы этого не сделаете и новая про|
грамма приведет к сбою в работе компьютера и к его перезагрузке, вся проделан|"
ная вами работа будет потеряна.

56

Часть I. Первое знакомство с Visual C++ .NET

В процессе написания кодов вы можете заметить, что некоторые действия приходится
выполнять снова и снова. Одни из них относятся к разряду обычных (например, вырезание
или копирование текста). Другие возможны только при написании программ (например, одновременное смешение группы строк или открытие заголовочного файла). В табл. 5.1 приведен список наиболее часто выполняемых задач и описание способов их решения. Более специфические задачи описаны после более общих.
Таблица 5.1. Задачи редактирования, решаемые в процессе написания кодов программ
Задача

Способ решения

Выделение текста

Щелкните в позиции, с которой должно начинаться выделение. Нажмите
левую кнопку мыши, перетащите курсор к позиции, в которой выделение
должно заканчиваться, и отпустите кнопку мыши.
Или щелкните в позиции, с которой должно начинаться выделение,
нажмите и удерживайте клавишу и щелкните в позиции, в
которой выделение должно заканчиваться.
Или используйте клавиши управления курсором, чтобы перейти в
позицию начала выделения, нажмите и удерживайте клавишу и,
опять используя клавиши управления курсором, перейдите в позицию
окончания выделения

Вырезание текста

Выделите текст. Выберите команду E d i t ^ C u t (Правкаовырезать) или
нажмите клавиши . Можете затем вставить вырезанный текст в
любом другом месте. (Для вырезания текста можно также щелкнуть
правой кнопкой мыши на выделенном фрагменте и выбрать из
открывшегося контекстного меню команду Cut.)

Копирование текста

Выделите текст. Выберите команду E d i t ^ C o p y (Правка^Копировать)
или нажмите клавиши . Можете затем вставить скопированный
текст в любом другом месте. (Для копирования текста можно также
щелкнуть правой кнопкой мыши на выделенном фрагменте и выбрать из
открывшегося контекстного меню команду Сору.)

Вставка текста

Выберите команду EdhX>Paste (Правка^Вставить) или нажмите
клавиши . В окно редактора будет вставлен текст из буфера
обмена. (Можно также щелкнуть правой кнопкой мыши в позиции, где
должен быть вставлен текст, и выбрать из открывшегося контекстного
меню команду Paste.)

Переход в начало файла

Нажмите Home>

Переход в конец файла

Нажмите

Переход на страницу назад

Нажмите клавишу

Переход на страницу вперед

Нажмите клавишу

Переход вправо на одно слово

Нажмите «ЗгКстрелка вправо>

Глава 5. Хороший редактор — что еще нужно бывалому программисту?

Окончание табл. 5.1
Задача

Способ решения

Переход влево на одно слово

Нажмите

Одновременный сдвиг
нескольких строк

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

Отмена сдвига нескольких строк

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

Переход ко второй скобке из
пары скобок

Нажмите , чтобы перейти от открывающей скобки (, {, < или
[ - курсор уже должен находиться перед ней - к соответствующей ей
закрывающей скобке:),}, > или ]. Тем же нажатием выполняется
обратный переход.
Эта возможность полезна в случае, если вы имеете много вложенных
друг в друга операторов и хотите найти границы каждого из блоков.
Также таким образом можно проверять, не забыли ли вы закончить
вызов функции закрытием скобки

Использование закладок

Поместите курсор в том месте, где вы хотите оставить закладку.
Нажмите дважды < Ш + К > . Таким образом вы огметите позицию в
кодах программы, к которой при необходимости сможете быстро
вернуться.
Например, если вы хотите скопировать фрагмент кодов из одной части
программы в другую, установите закладку, найдите нужный фрагмент
кодов, скопируйте его, вернитесь обратно к закладке и вставьте
скопированный фрагмент

Переход к закладке

Нажмите и , чтобы перейти к следующей закладке в
файле. Нажмите и , чтобы перейти к предыдущей
закладке

Переключение между окнами
редактора

Нажмите или , чтобы перейти к следующему окну
редактора (которое было открыто после текущего). Нажмите
или , чтобы перейти к предыдущему
окну редактора (которое было открыто перед текущим)

Открытие заголовочного файла,
соответствующего открытому
файлу

Щелкните правой кнопкой мыши на команде # i n c l u d e и выберите из
открывшегося контекстного меню команду O p e n Document.

Получение справки о командах

Щелкните на том элементе программы, по которому нужно получить
справку. Нажмите клавишу .

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

Таким образом, например, можно получать справку относительно
синтаксиса вызова библиотек, Windows API или команд C++

58

Часть I. Первое знакомство с Visual C++ .NET

Зываюйь разные —
белые, красные
В Visual C++ есть возможность выделять цветом отдельные синтаксические элементы,
что значительно упрощает чтение набранных кодов. Выделив цветом отдельные элементы
программы, такие как комментарии, ключевые слова, значения и переменные, вы делаете для
себя их идентификацию автоматической. Этот прием также облегчает обнаружение типичных
синтаксических ошибок.
Например, если все коды программы будут отображаться черным цветом, а комментар и и — синим, вы сразу увидите, что забыли закрыть какой-то комментарий (поставив в его
конце символы * / ) , поскольку далее за ним будет открываться бескрайнее море синего текста. Если установить, что все ключевые слова должны отображаться зеленым цветом, и если
какое-то набранное ключевое слово не окрасится в зеленый цвет, вы сразу же поймете, что
при его наборе допустили синтаксическую ошибку. Например, если вы наберете c l a p s вместо c l a s s , набранное слово зеленым не станет. Точно так же, если при объявлении какая-то
переменная окрасится в зеленый цвет, вы сразу поймете, что это имя для нее неприемлемо,
поскольку имена переменных не должны совпадать с ключевыми словами. (Более детально
правила присвоения имен переменным будут рассмотрены в главе 8.) Чтобы определить цвета для различных синтаксических элементов программы, выберите команду ToolS^Options
(Сервис^Параметры) и во вкладке Environment (Окружение) перейдите к опциям Fonts and
Colors (Шрифты и цвета).
На рис. 5.2 показано окно редактора кодов с включенной возможностью выделения цветом. (Разумеется, поскольку вы смотрите на черно-белый рисунок, для вас это больше похоже
на выделение серым цветом.)

1ODO;addmemberinitialisationcodehere~
CChd
liFrame:~CChd
iFrame()
BOOLCChd
liFrame:PreCreateWn
idow(CREATEST
{
/,'TODO;ModfiytheWn
idowd.s
iborstylesherif(!CMDIChd
liWnd:PreCreateWn
idow(cs))
returnFALSE;
returnTRUE;
Рис. 5.2. Выделение цветом отдельных синтаксических элементов программы значительно облегчает их визуальную идентификацию

Глава 5. Хороший редактор — что еще нужно бывалому программисту?
59

Ломощь, KOtnofiasi всегда /гл^ом
Если вы используете C++, окружение .NET, библиотечные функции, среду Windows и не помните или не знаете, как работает то или иное средство, либо что обозначает тот или иной элемент,
нужная подсказка находится на "расстоянии" одного щелчка. Например, допустим, что вы используете библиотечную функцию c o u t , но не уверены в корректности такого выражения:
c o u t 10)
Можно щелкнуть на ключевом слове w h i l e , нажать клавишу и получить справку об
использовании команды w h i l e , а можно щелкнуть на слове s t r l e n , нажать клавишу и
получить справку об использовании библиотечной функции s t r l e n .

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

Щелкните на поле со списком, расположенном слева.
Откроется список используемых классов.
ChildFrm.cpp
^•CChildFrame

GetRuntimeClass
Y TODO: add member • AssertVaiid
• CChildFrame
• CreateObject
• Dump
CChildFrame::~CChildFr • GetRuntimeClass
• GetThisClass
{
•f^CreSeWsi
}
-•-CChiidFrame
BOOLCChildFrame::PreCreateWindow(CREATEST
{
/.,' TODO: Modify the Window rkn^s or stylos herif( !CMDIChildWnd::PreCreateWindow(cs) )
return FALSE;
return TRUE;

РИС. 5.З. Панель перехода автоматизирует переход к нужным элементам программы
60

Часть I. Первое знакомство с Visual C++ .NET

2.

Выберите интересующий вас класс.

3.

Щелкните на поле со списком, расположенном справа.
Откроется список функций, используемых в выбранном классе (рис. 5.3).

4.

Выберите функцию, коды которой хотите просмотреть или отредактировать.
Visual C++ переместит курсор к этой функции.

Обратите внимание, что панель перехода отображает только классы и функции, используемые в текущем файле. Если вы хотите просмотреть другие элементы классов, используйте
средство Class view.

Cacftbtffiue и отображение кодов
Иногда обилие кодов в открытых окнах нескольких проектов может создавать впечатление
хаоса. Разработчики Visual C++ учли эту проблему и проедложили способ ее решения. Те фрагменты кодов, которые в данный момент вас не интересуют, могут быть свернуты, чтобы не занимать место на экране. А если вы снова захотите их просмотреть, их отображение можно быстро восстановить. Набор строк, который одновременно может быть свернут или вновь отображен, называется фрагментом кодов. Фрагменты могут быть созданы автоматически, либо вы
сами можете их определить. Рядом с каждым фрагментом расположен знак "плюс" или "минус",
позволяющий отображать либо сворачивать нужный фрагмент (рис. 5.4).

j*J CMan
i Frame

MainFrm.cpp
_•] j •PreCreateWindow

CMainFrame::CMainFrame(),..L
CMainFrame::~CMainFrame().,?i
! int CMainFrame::OnCreate(LPCREATESTRUCT IpCi
BOOL CMainFrame::PreCreateWindow(CREATESTI
{
if( !CMDIFrameWnd::PreCreateWindow(cs) )
return FALSE}
return TRUE;

void CMainFrame::AssertValid()..,
Рис. 5.4. Свернув коды, вы можете видеть "общую картину"
Существует два основных способа свертывания кодов.
!; ^ Свертывание произвольных фрагментов. Вы можете легко свернуть любой нуж\
ный вам набор кодов. Для этого просто выделите его, щелкните на нем правой кноп1.
кой мыши и выберите команду Outlining^Hide Selection (Свернуть1^Скрыть выде|
ленное). Можно также выделить нужный набор кодов и в главном меню выбрать ко|
манду Edit^Outlining^Hide Selection (Правка 1 ^Свернуть^Скрыть выделенное).

Глава 5. Хороший редактор — что еще нужно бывалому программисту?
61

Свертывание блоков. Visual C++ автоматически выделяет структурные элементы языка С+-. Таким образом вы можете сразу сворачивать отдельные блоки программы.
(Более подробно о блоках программы, к которым относятся циклы, операторы управления потоками данных и функции, речь идет в части П книги.) Чтобы пол>чить возможность сворачивать отдельные блоки, выберите команду Edit^Outlining^Collapse to
1
Definitions (Правка ^ Свернуть^ Сворачивать по определениям). Visual C++ выделит в
отдельные фрагменты каждое определение функции, определение класса и все прочие
блоки, имеющиеся в кодах открытого в данный момент окна. Также будут выделены все
блоки, входящие в состав дргих блоков.

JIoucic и авйгозамеяа
В процессе написания программ вы очень часто будете сталкиваться с необходимостью
поиска некоторых уже набранных кодов. Вместо того чтобы самостоятельно просматривать
коды всей программы в поиске нужного фрагмента, доверьте это занятие редактору кодов.
Чтобы найти какой-то фрагмент текста программы, воспользуйтесь командой Edit^Find
and Replace^Find (Правка^! 1оиск и замена^Найти) или нажмите клавиши . Если
найденный фрагмент текста нужно сразу же заменить каким-то другим, примените команду
Edit^Find and Replace^Replace (Правка^Поиск и замена^Заменить) или нажмите комбинацию клавиш . Например, можно использовать команду Edit^Find and Replace
^Replace, чтобы найти в тексте программы все упоминания переменной foo и заменить их
переменной goo.
Диалоговые окна Find (Поиск) и Replace (Замена) позволят вам определить множество
дополнительных установок, контролирующих процесс поиска нужных фрагментов в тексте
программы. Диалоговое окно Find показано на рис. 5.5.
Откуда берутся все эти загадочные термины
Подстановочные знаки иногда называют regular expression matching commands {команды сопоставления ре= гулярных выражений). Этот громоздкий термин появился благодаря некоторым особенностям работы компи\ ляторов.
При построении компиляторов создаются так называемые лексические анализаторы - специальные программы, просматривающие текст исходных файлов и разбивающие его на отдельные составляющие, которые компилятор сможет как-то интерпретировать. Разбивка текста файлов осуществляется на основании сопоставления его с заранее определенными образцами {или шаблонами). Эти шаблоны и называ• ются регулярными выражениями.
Таким образом, сопоставление регулярных выражений обозначает всего лишь поиск и идентификацию
текста, соответствующего заданным шаблонам.
Но вы можете называть это просто использованием подстановочных знаков.
Регулярные выражения, представленные в табл. 5.3, иногда называются регулярными выражениями GREP,
поскольку они используются программой GREP - одной из наиболее распространенных программ поиска
текста. GREP стала популярным инструментом именно в системах UNIX. А как известно, многие инструменты
UNIX имеют такие своеобразные названия, как MAWK, SED, DIFF, или как GREP. В разговоре с каким-нибудь
хакером, можете спокойно использовать слово grep вместо слова поиск, если вы имеете в виду поиск текста
по заданному шаблону. Если это настоящий хакер, он вас поймет.

62

Часть I. Первое знакомство с Visual C++ .NET

Find
! Find what:

'fane and riches

, Г* Match c,ase

Search

Г" Match whole word
; Г" Search hidden text

All open documents

• Г" Search up
Г Use;

Рис. 5.5. Укажите, что нужно найти, и вы немедленно получите результат
Ниже приведен список основных опиий диалоговых окон Find и Replace.
Match case (С учетом регистра). В языке О + строчные и прописные буквы считаются разными. Например, слова Boogs и boogs рассматриваются как названия
двух разных переменных. Если вы активизируете опцию Match case, будут найдены (или заменены) только те слова, в которых очередность строчных и прописных
букв точно совпадает с заданным образцом. Используйте ее только в том случае,
если точно знаете, какое слово должно быть найдено (учитывая строчные и прописные быквы). Если есть какие-то сомнения, отключите эту опцию.
Match whole word only (Только целые слова). Если эта опция активизирована, обрабатываться будут только отдельные целые слова (слова, которые отделены от
других слов пробелами, запятыми или скобками). Если вы эту опцию отключите,
найден будет также тот текст, который совпадает со сравниваемым образцом и является составной частью другого слова. Например, если вы отключите эту опцию и
укажете для поиска текст c o n s t , он будет найден в таких словах, как c o n s t a n t и
deconstruct.
• Search hidden text (Просматривать скрытый текст). Если вы активизируете эту
опцию, просматриваться также будет скрытые (в результате сворачивания) фрагменты текста.
Search up (Найти выше). Обычно поиск ведется от текущей позиции курсора вниз
до конца файла. Эта опция указывает редактору, что нужно просматривать текст от
текущей позиции курсора вверх до начала файла.
Use regular expression and wildcards (Использовать регулярные выражения и
подстановочные знаки). Подстановочные знаки— это специальные символы, которые используются для представления других символов. Например, предположим,
что нужно найти какую-то переменную и вы не помните точно ее названия, но
знаете, что оно начинается с буквы. В таком случае при указании образца для поиска наберите между буквами S и h подстановочный знак * (звездочка), который
заменит собой произвольное количество любых других символов.
Подстановочные знаки являются упрощенным вариантом более гибкого инструмента— регулярных выражений. В табл. 5.2 приведен список наиболее часто
употребляемых подстановочных знаков. В табл. 5.3 вы найдете перечень основных
регулярных выражений.
Search: Current window^Current document (Поиск: Текущее окно^Текущий документ). Эта опция сообщает редактору, что нужно просматривать только тот
файл, который открыт в текущем окне.
Search: All Open documents (Просмотр всех открытых документов). Эта опция
сообщает редактору, что нужно просматривать все открытые в данный момент
файлы. Эта возможность может быть полезной, например, если вы хотите изменить имя класса, используемого в разных файлах проекта.

Глава 5. Хороший редактор - что еще нужно бывалому программисту?

63

Search: Only (Просмотр только текущего блока). Поиск может
быть ограничен пределами блоков, на которые разбивается текст или коды. Если
поиск в пределах блоков будет возможен. Visual C++ сделает эту опцию доступной
и укажет параметры этого поиска.
Search: Selection only (Просмотр только выделенного фрагмента). Эта опция позволяет заменить текст только в выделенном в данный момент фрагменте. Это удобно,
если необходимо, например, изменить имя переменной для какой-то одной функции.
Mark All (Пометить все). Щелчок на этой кнопке дает указание Visual C++ найти
все соответствия, но не отображать их на экране, а просто пометить, с тем чтобы
впоследствии вы могли к ним вернуться.
Таблица 5.2. Подстановочные знаки
Команда

Значение

'••

Заменяет один произвольный символ. Например, шаблону s ? i p соответствуют слова S k i p ,
Swip и т.п.

*

Заменяет любое количество произвольных символов. Например, шаблону s i * сооветствуют
слова S I , S l i p , S l i d i n g и др.

!

-]

Заменяет один из символов, указанных с скобках. Например, шаблону S [ m l ] ug
соответствуют слова Smug и Slug

',! ]

Заменяет любой символ, за исключением тех, что указаны в скобках. Например, шаблону
S [ - m l ] ug соответствуют такие слова, как s t u g , Swug, но не Smug и S l u g

Таблица 5.3. Команды сопоставления регулярных выражений
Команда

Значение
Заменяет собой любой один символ. Например, шаблону s . ip соответствуют слова S k i p ,
Swip и т.п,

*

Заменяет любое количество произвольных символов. Например, шаблону s i * соответствуют
слова S I , S l i p , S l i d i n g и др.

+

Заменяет любое количество символов, предшествующих (по алфавиту} символу, после
которого эта команда набрана. Например, шаблону So* соответствуют слова Soon, Son,
So и др.
Ищет соответствие по началу строки. Например, шаблон Л / / находит все комментарии,
которые начинаются от начала строки

$

Ищет соответствие по концу строки. Например, шаблон f оо$ находит только те слова f оо,
которые набраны в конце строки

['-

Заменяет собой один из символов, набранных внутри скобок. Например, шаблону s [ m l ] ug
соответствуют слова Smug и Slug

['" ]

Заменяет собой любой символ, кроме тех, что набраны внутри скобок. Например, шаблону
Б [ Л т 1 ] и д соответствуют слова Saug, Skug и другие, но не Smug и S l u g

[ - ]

Заменяет собой любую букву из диапазона, указанного в скобках (включая сами набранные
буквы). Например, шаблону S [ c - l ] u g соответствуют только слова из набора Scug,
Sdug, Seug, . . . Slug

64

Часть I. Первое знакомство с Visual C++ .NET

Глава 6

Компиляция программ, или
Первые трудности
главе...
> Компиляция программ
> Синтаксические ошибки и предупреждения
> Различие между компиляцией, построением и повторным построением

'ГТ аписание кодов программ может быть весьма увлекательным занятием. В набранные
• •* формулы и выражения можно вложить самый разнообразный смысл, затем вывести
все эти коды на печать и развеселить ими своих друзей. Например, с помощью команд и операторов можно перевести на язык программирования хорошо всем известные крылатые фразы. Если бы Шекспир был программистом, он мог бы написать что-то наподобие if (_2В)
{} e l s e {}. (А если бы он был толковым хакером, он написал бы_2В ? {} : { }.)
Однако, если вы хотите, чтобы программа была действительно полезной и выполняла какие-то действия, одного лишь красивого текста недостаточно. Программу нужно откомпилировать. Компиляция программы — это преобразование исходных кодов программы, которые
понятны программистам, в машинные коды, которые понятны компьютерам.
Процесс преобразования исходных кодов в машинные крайне сложен. Он требует точного
сопоставления всех набранных вами инструкций (которые называются инструкциями высшего уровня) соответствующим инструкциям нижнего уровня (понятным компьютеру). Этот
процесс завершается созданием выполняемой программы. Именно она может повлечь за собой какие-то реальные действия.
О процессе преобразования набранных программистами команд в машинные коды написано множество книг. К счастью для вас, разработчики, занимающиеся созданием компиляторов, все эти книги уже прочитали, поэтому вам вникать в различные тонкости этого процесса не нужно. У вас есть Visual C++, который сам откомпилирует набранные вами коды.

Наншйь /сом/гили/10#а/нб nftotftaMMif

оченьnftoc/no
Процесс создания программы включает набор кодов, компиляцию и отладку- В большинстве
случаев происходит также корректировка работы программы и добавление к ней новых возможностей, что влечет за собой многократную повторную компиляцию. К счастью, в Visual C++ компилировать программы очень просто. В действительности вы уже компилировали программу (и не
один раз), если выполняли инструкции, описанные в главе 3.
Чтобы откомпилировать программу, откройте ее решение (solution). (Решения подробно
описаны в главе 4.) Затем выберите команду Build^Build (Построить^Построить) или щелкните на кнопке Build одноименной панели (рис. 6.1).

Глава 6. КОМПИЛЯЦИЯ программ, ИЛИ Первые трудности

65

Bulk*
Рис. 6.1. На этой панели расположены кнопки, которые запускают процесс компиляции
Visual C++ просматривает все исходные файлы открытого решения и преобразует их к
машинным кодам. В результате создается выполняемая программа. На экране вы не увидите
никаких изменений, но теперь у вас уже есть реальная программа, которая хранится на жестком диске. Ее можно запустить на выполнение, скопировать на дискету и подарить комунибудь, а можно делать с ней все, что вы считаете нужным. (Только не предпринимайте ничего такого, что может повредить вашему здоровью или здоровью окружающих.)
Если вы хотите запустить программу, выберите команду Dedug^Start (Отладка^ Запустить)
или щелкните на кнопке Start панели Dedug (рис. 6.2). Если вы уверены, что в программе нет
ошибок (это равносильно тому, что вы считаете себя непревзойденным хакером), выберите команду Debug^Start Without Debugging (Отладка^Запустить без отладки), чтобы запустить программу без использования отладчика Visual C++.

Рис. 6.2. На этой панели есть кнопки для запуска,
остановки и пошагового выполнения программы

Синтаксические OUIUJKU: неужели
их можешь ЯьмЯь *нак много?/
Если при написании кодов программы вы допустили какую-то ошибку, например передали функции неверное количество аргументов, неправильно набрали команду или присвоили
недопустимое имя переменной, компилятор не сможет понять ваши коды и программа не будет откомпилирована.
Если это случится (правильнее сказать: когда это случится, поскольку случается это практически всегда), компилятор отобразит список синтаксических ошибок в окне Output, как
показано на рис. 6.3. Синтаксическая ошибка в окне Output сопровождается сообщением о
том, что вы что-то сделали неправильно. Дважды щелкните на этом сообщении, чтобы перейти к той строке кодов программы, где эта ошибка произошла. Чтобы программа была успешно откомпилирована, необходимо исправить все синтаксические ошибки.

TaskLt»t? 0ЫИ Егг«>ю»з shown (f fltered)
1
'' Description

' ф
•&

ф

VX
fie

error Lf4K200S: "public: v ntual void thiscall CMarf ame :Asset tVall*vo«J)con KiHerApp
error LNK2O0S: "puck: v rtual vO'd fMrscall CMan
i F ame::Dump(da5S CDumpC KillerApp
Fetal error LMKllr59- one or more multiply defined syncols lound
CillerApp
1
nt', possible loss of data с:'.Documents and ,,.\KillerApp^MainFrm c p p
warning C*2W 'return' conversion from 'dousle' to

Line

л

. .
46

v

в. - -. 21 т*л u« |
РИС. 6.3. Сообщения о синтаксических ошибках, которые отображаются в окне Output
ОДНИ ошибки очевидны и могут быть сразу же исправлены. Другие не так просты, и вам
придется потратить немало времени, чтобы их обнаружить и исправить (особенно если у вас
еще нет в этом большого опыта). Со временем вы научитесь быстро различать типичные
ошибки и сразу же их исправлять.

65

Часть I. Первое знакомство с Visual C++ .NET

Вот несколько правил, которые помогут вам в борьбе с ошибками.
> •/ Сохраните файлы программы, перед тем как приступить к их компиляции.
I S Если вы никак не можете найти ошибку в какой-то строке, проверьте предыдущую
ш
строку. Бывает, что ошибка "сидит" именно там.
Г S Многие ошибки случаются из-за недостающих или лишних фигурных скобок ({ })
|
или точек с запятой (;).
1 •/ Компилируйте программу небольшими частями. Это позволит вам сосредоточитьI
ся на отдельных фрагментах, а не рассеивать внимание по всей программе.
\ S Сообщение C a n n o t c o n v e r t from . . . to . . . (Нельзя преобразовать ... к ...)
обычно появляется, если вы пытаетесь присвоить переменной значение не того типа.
Например, если текстовой переменной вы пытаетесь присвоить значение типа integer.
Убедитесь, что вы правильно набрали имена переменных. Очень многие ошибки
происходят именно из-за неверного указания имен переменных.
Просмотрите главы 23 и 24. В них вы найдете перечни наиболее часто встречающихся ошибок и способы их устранения.
Иногда одна небольшая проблема может привести к тому, что компилятор обнаружит
сразу "тысячу" ошибок. Например, неправильно указанный путь к заголовочному
файлу может стать причиной появления нескольких десятков сообщений об ошибках.
(Обычно это случается при создании неуправляемых программ.) Достаточно устранить причину— и многие ошибки сразу же отпадут. Поэтому, если вы компилируете
программу и на вас обрушивается целый поток сообщений об ошибках, не паникуйте.
Скорее всего, большинство из них можно исправить одним простым действием.
I •/ Если вы никак не можете понять, в чем заключается ошибка, попросите когоI
нибудь о помощи. Иногда свежий взгляд на вещи помогает легко решить казалось
|
бы неразрешимую проблему.

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

Ouspu*
Build
StudioP
: warning C4Z44 'return'
from do Ые
StudioProjects\K !erApp\MainFrrri.cpp(71): warning C4244:'return' conversionfrom Ые
do

Я X

H
Ые oss of data
to mt
to "inf poss Ые oss of data

<

V
i>

РИС 6.4. Подобные предупреждения появляются на экране, если набранные вами коды могут
привести к возникновению проблем
Относитесь к предупреждениям с должным вниманием. Хотя иногда они могут появляться
как следствие чрезмерной осторожности компилятора, в большинстве случаев они помогают
избежать многих неприятностей. Например, если вы забудете добавить строку, задающую на-

Глава 6. Компиляция программ, или Первые трудности

67

чальное значение переменной, компилятор выдаст предупреждение. Если вы его проигнорируете, в процессе выполнения программы эта переменная будет гринимать совершенно бессмысленные значения, в результате чего программа, разумеется, будет работать неправильно.

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

можно по-/газноли/
/

Есть три способа компиляции файла: он может быть построен, откомпилирован
либо повторно построен.

При построении программы (команда Build^Build) обновляются файлы OBJ для всех измененных исходных файлов. Когда все файлы OBJ обновлены, программа компонуется для
создания нового выполняемого файла (ЕХЕ). Результатом такой компиляции будет либо набор сообщений о синтаксических ошибках, либо полностью рабочая программа.
Если вы работаете над проектом и вносите изменения только в некоторые его части, используйте именно этот способ компиляции. При этом заново откомпилированы будут только
измененные файлы и вам не нужно будет тратить массу времени и ждать, пока компилятор
сделает бессмысленную работу и еще раз откомпилирует те файлы, которые вы не трогали.
Поэтому используйте команду Builds Build, если вы внесли какие-то изменения в исходные
файлы и вам нужен теперь новый файл ЕХЕ.
При обычной комтияции (команда Build^Compile) компилируется только тот файл, который
открыт в данный момент для редактирования. Эгой командой не создается рабочая программа.
Используйте ее в том случае, если хотите проверить какой-либо исходный файл на наличие синтаксических ошибок. Если компиляция проходит успешно, создается объектный файл (OBJ).
При повторном построении (команда Builds Rebuild All) все файлы проекта компилируются и затем компонуются для создания выполняемой программы (ЕХЕ). Как и в случае с построением, результатом такой команды будет либо набор сообщений о синтаксических
ошибках, либо полностью рабочая программа. Даже если вы только что построили программу, при повторном построении каждый отдельный файл компилируется заново. (Результатом
такой операции будут обновленные объектные файлы всех исходных файлов плюс выполняемый файл программы, созданный путем компоновки всех объектных файлов.) Используйте
эту команду в том случае, если хотите быть уверены, что весь проект был построен заново.

68

Часть I. Первое знакомство с Visual C++ .NET

Часть II

Все, что вы хотели знать о C++,
но о чем боялись спросить

МОЖНО

v\tp&bW

В э&ой час&и...
С этой части начинается серьезное обучение; Вы получите
фундаментальные знания о Visual C++, начиная с того, как укомплектовать программу переменными, операторами и указателями. Также вы
познакомитесь с одним из наиболее важных инструментов создания
программ — отладчиком.
Ни в коем случае не игнорируйте приводимые примеры, поскольку
многое вещи познаются на практике. Пробуйте самостоятельно набрать
коды программ и выполнить их на своем компьютере, чтобы быть
уверенным, что вы правильно понимаете излагаемьгй здесь материал.
Сразу скажем, что не нужно пугаться этой части, поскольку сухость и
серьезность рассматриваемых здесь вопросов компенсируется хорошим,
:
добрым юмором.
V

Глава 7

Типы данных — это серьезно
Вэ/Яой иа£е...
>

Строгие и нестрогие языки программирования

>

Объявление типов переменных

У* Преобразование строк в числа
>

Создание констант

>* Работа со строками

^ к о м п ь ю т е р н ы е программы занимаются обработкой различных данных, которые, в свою
/ V* очередь, могут принадлежать различным типам. Например, в электронных таблицах
данные могут быть представлены числами с плавающей запятой, в инвентаризационных
системах— порядковыми записями, а в программах для рисования— автофигурами. Однако
нужно понимать, что, хотя режиссеры Голливуда и создают мифы о всемогущих и всезнающих
компьютерах, на самом деле 'это не так. Компьютер может работать только с данными строго
определенных типов, причем предварительно (гужно указать, данные какого именно типа будут
ему передаваться. В этой главе вы познакомитесь с основными типами данных, особенно
детально обсуждается один из самых важных, который обозначается словом S t r i n g .

С/н/гогие и неоплате
Строгие языки программирования, такие как C++, требуют, чтобы программисты заранее
объявляли, данные каких типов они собираются использовать. Например, если вы хотите сохранить число, нужно заранее сообщить об этом компьютеру, чтобы он был готов принять от
вас это число.
Строгие языки имеют ряд неоспоримых преимуществ. Например, если вы случайно попытаетесь запись о сотруднике интерпретировать как число, компилятор выдаст сообщение об
ошибке. Это хорошо, поскольку без подобного контроля вы можете непреднамеренно уничтожить важную информацию. Предположим, запись о сотруднике занимает 8 байт памяти, а
для числа отводится только 2 байта. Когда вы удаляете запись, освобождается 8 байт памяти.
Если же вы используете эту запись как число и затем это число удалите, помимо числа будет
удалено еще 6 байт памяти, в которых может содержаться очень важная информация. Возможно, потеря этой информации будет иметь для вас очень плохие последствия.
Помимо строгих языков программирования, есть также нестрогие, к числу которых относится, например, JavaScript. Они не требуют предварительного объявления типов данных и
сами оценивают данные в тот момент, когда с ними сталкиваются. Например, если вы присвоите переменной текстовое значение r o o t , языковой процессор сделает эту переменную
текстовой. Нестрогие языки программирования могут быть проще в использовании, и некоторые программисты отдают им предпочтение, однако они не умеют предостерегать вас от
ошибок, как это делают строгие языки.

Глава 7. ТИПЫ данных — это серьезно

71

ОЗшвлениепе/геменных
В языке C++ необходимо заранее объявлять переменную и указывать тип используемых
ею данных. Другими словами, нужно сразу указать компилятору, значения какого типа могут
быть присвоены переменной. (Более подробно об использовании переменных речь идет в
главе 8. Пока же рассматривайте переменную как нечто, способное сохранять информацию,
например как ячейку электронной таблицы, которая, однако, имеет свое собственное имя.)
Объявить тип переменной можно несколькими способами, но обычно это делается в момент
ее создания.
Процесс создания переменной называется ее определением. Чтобы определить переменную, нужно просто указать используемый ею тип данных, а затем ее имя. Ниже приведено
несколько примеров определения переменных. В следующей строке создается переменная
foe типа i n t (integer, число):
int

too;
Далее создается переменная b a r типа c h a r (character, символ):

char

bar;

И ниже сообщается о том, что функция min принимает два числа типа i n t в качестве
значений параметров и возвращает другое число типа i n t в качестве результата. (Более подробно определение и использование функций рассматривается в главе 12.)
int min{int f i r s t , i n t second);
Обратите внимание, что объявление переменной отличается от ее опреоеления.
Когда вы объявляете тип данных, вы просто указываете, значения какого типа
будет принимать эта переменная. При этом выделения памяти для объявляемой
переменной не происходит. (Физически переменная не создается.) Когда вы чтото определяете, вы создаете это физически. В случае с переменными это различие
не столь принципиально. Почти всегда при определении переменной указывается
и ее тип. Хотя делать это можно и раздельно. Познакомившись со структурами
(глава 9) и классами (глава 17), вы увидите, что для них определение и объявление — это два разных действия, каждое из которых выполняется отдельно.

Наиболее часто используемые типы данных
В языке C++ имеется набор заранее определенных типов данных, которые сразу можно
использовать. Используя стандартные типы данных, можно создавать собственные, более
сложные, типы данных, что описывается в главе 9. Вот три наиболее часто используемых типа данных.
j S double. Число с плавающей запятой. (Не думайте, что это число с водой, в которой плавает запятая. Это всего лишь вещественное число с десятичной запятой, которая может ''плавать'" в зависимости от количества десятичных знаков в числе и
не имеет точно зафиксированной позииии. Сюда относятся, например, такие числа,
как 4,3444; 42,1; 3,И.) Переменные такого типа могут содержать значения в пределах от ± 1,7*1(Гзс8до± 1,7*1О+зот.
i n t . Целое число. Значение может быть положительным, отрицательным либо нулевым. Например, к этому типу относятся значения 0, 1, 39, -42, но не 1,5.
S t r i n g . Любой текст, например H e l l o World.

72 Часть II. Все, что вы хотели знать о C++, но о чем боялись спросить

Например, вам нужна переменная для хранения значения радиуса круга. Можете определить ее таким образом:
double dblRadius;
Этой строкой создается переменная d b l R a d i u s , которая может принимать значения типа d o u b l e (числа с плаваюшей запятой). Для сохранения значения радиуса круга такая переменная вполне подойдет.
Перед созданием программы определите для себя, переменные каких типов вам потребуются для хранения всех тех данных, которые вы собираетесь использовать. Например, для
сохранения числа, обозначающего количество людей, посетивших концерт, можно создать
переменную типа i n t (целое число), поскольку, разумеется, количество людей не может
быть дробным. (Правда, кто-то может прийти со своей "второй половиной", но это для него
она "половина", а для всех остальных— отдельный человек.) Если же вам нужна переменная
для сохранения значения радиуса крута, лучше использовать тип double, поскольку радиус
вполне может обозначаться дробным числом. И если вам нужно сохранять текстовую информацию, создайте переменную типа S t r i n g .

Реже используемые типы данных
Ниже дается описание некоторых других типов данных, которые тоже используются при
создании программ, но не так часто, как приведенные выше.
I S f l o a t . Вещественное число с плавающей запятой; однако переменные этого типа
\
данных могут принимать значения из диапазона гораздо меньшего, чем перемен|
ные типа double. Для этого типа допустимые значения лежат в интервале от
\
±3,4*10~38до±3,4*10'зв.
| V
|
?
!

long. Это слово может быть добавлено перед словом i n t , чтобы обозначить, что
для хранения одного значения должно быть выделено 32 бит памяти. В Visual C++
.NET такой объем памяти выделяется по умолчанию, поэтому в использовании этого слова нет необходимости.

ё S s h o r t . Это слово может быть добавлено перед словом i n t . чтобы обозначить,
|
что для хранения одного значения должно быть выделено 16 бит памяти.
[ •/ s i n g e d . Это слово может быть добавлено перед словом i n t , чтобы обозначить,
I
что принимаемые значения могут быть как позитивными, так и негативными. (В
|
Visual C++ эта возможность предусмотрена по умолчанию.)
| •/
1
I
I
|
|

u n s i g n e d . Это слово может быть добавлено перед словом i n t , чтобыобозначить, что все принимаемые значения будут только позитивными. Благодаря этому
освобождается один дополнительный бит памяти для обозначения величины принимаемого значения. Так, если переменная типа i n t может принимать значения в
диапазоне от -2 147 483 648 до +2 147 483 647, то слово u n s i g n e d определяет
диапазон от 0 до 4 294 967 295.

е

|^
|
'? S
I
|

void. Тип не определяется. Используется для обозначения того, что функция не
возвращает никакого результата. (Более подробно об этом речь идет в главе 12.)
c h a r . Символ. А, В или & — это все символы. Этот тип данных чаще всего используется в неуправляемых программах, в то время как в управляемых в основном
применяется более гибкий тип S t r i n g .

Глава 7. Типы данных — это серьезно

73

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

Тип, который отличается от всех остальных
Чтобы убедиться, что все объявленные типы используются корректно, Visual C++ проделывает большую
работу. Во время компиляции исходного файла формируется так называемая таблица имен. Она содержит подробную информацию обо всех переменных и функциях, используемых в исходном файле. Каждый
раз, когда переменная (или функция) как-то используется, компилятор находит ее описание в таблице
имен, проверяет объявленный для нее тип данных и определяет, возможно ли в отношении переменной
(или функции) предпринимать такие действия.
Компилятор имеет также набор строгих правил, определяющих, как правильно преобразовать значения
одного типа к значениям другого. Например, если функции требуется для работы значение параметра
типа d o u b l e {более детально передача функции значений параметров описана в главе 12), а вы передаете ей значение типа i n t , компилятор сам сможет преобразовать целое число к числу с плавающей
запятой. Если же он обнаружит несоответствие типов, для которых не определено правило взаимного
преобразования, будет возвращено сообщение об ошибке.
При компиляции программы, состоящей из нескольких исходных файлов, ситуация усложняется. В
одном исходном файле могут использоваться переменные или функции, объявленные в других файлах. Чтобы убедиться в правильности использования типов данных, компилятор теперь должен сравнивать переменные и функции из разных файлов. Строго говоря, этот шаг выполняется уже на этапе
компоновки программы.

;

Проверка правильности использования типов данных (так называемый внешний анализ) осуществляется
с использованием техники, которая называется корректировкой имен. Когда вы объявляете переменную,
функцию или любой другой элемент, компилятор несколько изменяет присваиваемое вами имя (это имя
также называют внешним). Внешнее имя содержит информацию о типе элемента. Можно сказать, например, что компилятор добавляет к исходному имени букву i, чтобы обозначить, что элемент имеет тип
i n t , и буквы с р , чтобы обозначить, что элемент имеет тип c h a r . Далее предположим, что а одном из
исходных файлов вы создали такую функцию:

i n t NumberOfSongs();
В другом исходном файле вы набрали такой код:

char
MyText = NumberOfSongs{);
Компилятор преобразует имя N u m b e r O f S o n g s к имени i N u m b e r O f S o n g s , а имя M y T e x t - к
имени c p M y T e x t . Сами вы никогда не увидите внутренних имен (разве что посмотрите на листинг кодов языка ассемблера), но компоновщик видит их очень хорошо.
Для него, например, последняя строка кодов выглядит так;

CpMyText = iNumberOfSongs{);
Сравнивая скорректированные имена, компоновщик фиксирует несовпадение типов (поскольку символ и
число - это разные данные) и выдает сообщение об ошибке.

74 Часть II. Все, что вы хотели знать о C++, но о чем боялись спросить

Однако О + заботится о правильном использовании типов данных. Если вы объявите для
переменной один тип данных, а затем попробуете использовать ее как переменную другого
типа, компилятор выдаст сообщение об ошибке. Эта возможность называется обеспечением
типовой безопасности и является преимуществом языка О + , поскольку таким образом автоматически выявляются наиболее распространенные ошибки. (Это действительно лучше,
чем игнорирование компилятором неправильного использования типов данных, за исключением разве что тех случаев, когда вы создаете самоуничтожающуюся программу.)
Приведенный ниже код будет работать корректно.
//Создание переменной типа i n t
int nSize;
//Присвоение переменной значения 7
n S i z e = 7;
Но, если вы наберете следующий код, компилятор выдаст сообщение об ошибке, поскольку переменная типа i n t не может принимать текстовые значения:
//Попытка присвоить переменной т е к с т о в о е значение
n S i z e = " H e l l o World";
Иногда компилятор автоматически преобразует значения одного типа данных к значениям
другого типа. В приведенном ниже примере число с плавающей запятой автоматически преобразуется к целому числу:
i n t nSize;
nSize = 6.3;
После выполнения этого кода переменной n S i z e присваивается значение 6. (Компилятор
автоматически округляет число. Все цифры, расположенные справа от десятичной запятой,
просто отбрасываются.) Однако при этом компилятор отображает на экране предупреждение
о том, что будет выполнено преобразование чисел. Хорошие и по-настоящему качественные
программы должны компилироваться без предупреждений и ошибок. Чтобы избежать подобных предупреждений, можно явно указать компилятору на необходимость преобразования
одного типа данных к другому. Для этого перед переменной, значение которой должно быть
преобразовано, в круглых скобках укажите требующийся тип данных. Например:
int nSize;
double BigSize;
nSize = (int} BigZise;
В этом случае компилятор без лишних предупреждений преобразует значение типа
double к значению типа i n t . Это означает, что дробная часть значения переменной
B i g Z i s e будет просто усечена. Например, если эта переменная имеет значение, равное числу 3,141592653, переменной n S i z e будет присвоено значение, равное числу 3.

*пипов
В стандартном C++ типы данных являются просто структурами, которые требуют для себя
некоторого количества памяти. С другой стороны, в среде .NET типы данных обладают еще и
встроенной функциональностью. Технически они являются объектами. (Более подробно объекты описаны в главе 17.) Как и другие объекты, типы данных имеют встроенные функции, наиболее полезными из которых являются функции преобразования одних типов в другие. В
табл. 7,1 приведен список функций преобразования типов для основных типов данных.

Глава 7. ТИПЫ данных — это серьезно

75

Таблица 7 . 1 . Функции преобразования типов данных

Тип

Функция

Выполняемое действие

String

ToDouble{)

Преобразует тип s t r i n g к типу double

String

Tolnt32()

Преобразует тип S t r i n g к типу i n t

double

ToStringO

Преобразует тип d o u b l e к типу S t r i n g

double

Parse()

Преобразует тип S t r i n g к типу d o u b l e

int

ToStrinaO

Преобразует тип i n t к типу S t r i n g

int

Parse

Преобразует тип S t r i n g к типу i n t

Предположим, например, что вам нужно отобразить на экране число. Для этого предварительно необходимо преобразовать его в строку:
i n t nNumber = 3;
Console::WriteLine(nNumber.ToString{));
Теперь предположим, что у вас есть текстовое значение ( S t r i n g ) и из него нужно сделать число (double). Делается это так:
S t r i n g *szNumber = S " 3 . 1 4 "
d o u b l e dNumber;
dNumber - D o u b l e : : P a r s e ( s z N u m b e r ) ;
Вы, наверное, обратили внимание на использование звездочки (*) перед именем
текстовой переменной. Она там нужна, поскольку при работе со строками приходится использовать указатели. Более подробно указатели описаны в главе 13. Пока же просто примите к сведению, что при объявлении текстовых переменных
перед их именем нужно добавлять звездочку (*). И каждый раз при вызове встроенных для типа S t r i n g функций вместо точки (.) используйте символы ->.
Если вас интересуют другие подробности приведенного выше кода, прочитайте
этот абзац. Буква 3 перед значением " 3 . 1 4 " указывает компилятору, что это
должна быть строка, предусмотренная для использования в среде .NET. На самом
деле можно создавать текстовые значения самых разных видов, но в данном случае создается именно такое значение, которое для среды .NET подходит наилучшим образом. Использование двух двоеточий (: :) в фрагменте Double : : P a r s e
является специальным синтаксическим приемом, позволяющим применять методы (такие, как P a r s e ) объектных типов (таких, как d o u b l e ) без предварительного создания самих объектов. В будущем вы сможете по достоинству оценить эту
возможность языка C++.

— riio, ч,*но никогда
не меняемся
Иногда при написании программы требуется многократно использовать одно и то же число или одну и ту же строку. Например, если вы знакомы с математикой, то знаете, что число
п всегда равно 3,141592.... Если вы составляете программу, вычисляющую ваш гороскоп, то
используемая в вычислениях дата вашего рождения также будет оставаться неизменной.
76

Часть U. Все, что вы хотели знать о C++, но о чем боялись спросит

В подобных случаях становится возможным создание констант. Сами константы — это
просто названия элементов, которые в процессе выполнения программы никогда не изменяются. Чтобы сделать элемент константой, начните его объявление со слова c o n s t . Например, следующим кодом создастся константа PI, значение которой постоянно будет равно
числу 3,141592:
c o n s t double PI = 3.141592;
Использование констант значительно облегчает чтение кодов ваших программ, поскольку
обычные числа можно заменить объясняющими их именами. Кроме того, это удобно, если
одно и то же значение должно быть изменено во всей программе. Для этого нужно просто
определять новое значение для соответствующей константы. Например, если вы вдруг решите приспособить написанную вами программу к некоторой виртуальной реальности, где пространство и время деформированы и число п равно, скажем, числу 7, вам нужно будет всего
лишь изменить значение соответствующей константы. При этом все вычисления, выполняемые вашей программой с участием числа п, автоматически будут использовать новое указанное значение, что избавляет вас от необходимости самостоятельно просматривать коды всей
программы и вносить нужные коррективы.
В кодах программы использовать константы можно везде, где может быть использован
элемент того же типа, что и сама константа. Например, если у вас есть константа, представляющая число с плавающей запятой, вы можете применять ее везде, где будет уместно использование числа с плавающей запятой. Так. константа PI может быть умножена на диаметр круга, в результате чего будет вычислена длина окружности.

Использование констант в кодах программы
Ниже приведен код программы, в которой используются константы и выполняется преобразование типов данных. В процессе ее выполнения пользователю предлагается указать радиус, в соответствии с которым вычисляется площадь круга. Полученный результат отображается на экране. Не поленитесь и создайте такую же программу. Для этого создайте новую
программу .NET, наберите приведенный здесь код и запустите программу на выполнение.
//CircleArea
//Вычисление площади по значению радиуса
#include "stdafx.h"
#using
using namespace System;
//С этой строки начинается выполнение программы
#ifdef _UNICODE
int wmain(void)
telse
int main(void)
#endif
{
daub]e fitRadius;
double i'ltArpa;

Глава 7. Типы данных — это серьезно

77

const double PI = 3.141592;
//Отображение вопроса о значении радиуса
Console::WriteLine(S"Укажите радиус круга");
//Получение отЕета и преобразование его к числу с
//плавающей запятой
fltRadius = Double::Parse(Console::ReadLine());
//Вычисление площади круга
fltArea = PI*fltRadius*iltRadius;
//Отображение результата на экране
//Обратите внимание, что для этого число должно быть
//предварительно преобразовано в строку
Console::WriteLine(3"Площадь круга составляет {0}
единиц.", Area.ToString () ) ;
//Ожидание, пока пользователь не остановит
//выполнение программы
Console::WriteLine(S"Нажмите клавишу Enter, чтобы
остановить выполнение программы");
Console : : ReadLir.e () ;
return 0;
}

Константы и борьба с ошибками
Еще одно преимущество использования констант: если вы случайно попытаетесь изменить их значения в процессе выполнения программы, компилятор заметит это и выдаст сообщение об ошибке. Предположим, например, что где-то в кодах программы вы набрали такую строку:
?! = 15;
Как только компилятор увидит этот код, он сразу же выдаст сообщение об ошибке, в котором будет сказано, что изменять значение константы нельзя. Своевременное обнаружение
и устранение таких, казалось бы, мелких ошибок позволяет избежать возникновения в дальнейшем более серьезных проблем.

С/н/to/cu/са/сoquftизнаиболееважных
!пипов данных
Строки (тип S t r i n g ) являются основными данными почти для всех создаваемых программ. По крайней мере почти ни одна программа не может без них обойтись. Чтобы эффективно их использовать, нужно владеть некоторым количеством не очень сложных приемов.
Основные их них описаны в табл. 7.2.

78 Часть U. Все, что вы хотели знать о C++, но о чем боялись спросить

Таблица 7.2. Способы обработки текстовой информации
Задача

Решение

Комбинирование двух строк

sResult = S t r i n g : :Concat ( s t l , s t 2 ) ;

Отображение
значениями

на

экране

текста

Сравнивание двух строк

со C o n s o l e : : W r i t e L i n e (3"3начение
Значение 2 | 1 } " , з н 1 , з н 2 } ;

1:

{0}

cResult = stl->Equals (st2) ;

Определение позиции, начиная с которой n O f f s e t = s t l - M r . d e x O f ( s t 2 ) ;
одна строка входит во вторую
Получение п символов начиная с m-й m R e s u l t = s t l - > S u b s t r i n g ( m , n ) ;
позиции
Определение длины строки

nLength = s t - > L e n g t h ;

Удаление лишних пробелов в начале и в R e s u l t = s t l - > T r i m ( ) ;
конце строки
В кодах программ, которые приведены на страницах этой книги, вы будете часто сталкиваться со строками. Ниже дан код небольшой программы, на примере которого вы можете
увидеть в лействии различные способы обработки текстовой информации,
//StringsiOl
'Некоаорые приемы работы со строками
#induce

"stciafx.h"

#using
using namespace System;
этой строки начинается выполнение программы
#ifdef jJNICODE
int wraain(void)
int main(void)
#endif
{

String *pszString;
int nNumber;
dcub i.e f 11Number;
//Комбинирование двух строк
pszS-ririg = String::Concat(3"Майкл", S" Джексон")
Cons з!е: :WriteLine(pszString);

Глава 7. Типы данных — это серьезно

79

//Преобразование типов и комбинирование строк
nNumber = 3;
pszString - String::Concat(Э"Преобразованное '
nNumber.ToString()};
Console::WriteLine(pszString);
//Преобразование строки к типу double
Console::WriteLine(S"Введите числе");
fItNumber = Double::Parse(Console::ReadLine()),
//Добавление к этому значению числа
fltNumber = fltNumber + 30.5;
//Отображение полученного результата на экране
Console::WriteLine(String: :Concat ^"Результат:
fltNumber.ToStringO ) ) ;
//Объединение строк и их отображение
Console::WriteLine(String::Concat(3"Один
", 3"Три"));
//Отображение с использованием символов форматирования
Console::WriteLine(S"Строка: {0}ХпЧисло: {1}",
S " M O H строка",

nNumber.ToString());

//Удаление лишних пробелов в начале и в конце строки
pszString = "
Hello World
";
Console: :WriteLine (S''CT-рока с пробелами: {0}\п
и без пробелов: {1}", pszString,
pszString->Trim());
//Определение символа середины строки
Console::WriteLine(S"Посередине строки {0}
расположен символ \"{1}\"", pszString,
pszString->Substring (pszSt rir.g->Length/2, 1) ) ;
//Ожидание, пока пользователь не остановит
//выполнение программы
Console::WriteLine(S"Нажмите клавишу Enter, чтобы
остановить выполнение программы")
Console: :ReadLine() ;
return 0;

80 Часть II. Все, что вы хотели знать о C++, но о чем боялись спроси

Глава 8

Использование переменных
главе...
> Имена переменных
> Определение и инициализация переменных
V Соглашения о присвоении имен переменным

рограммы получают, обрабатывают и возвращают данные. Если нужно сохранить
какое-то значение или результат каких-то вычислений, используются переменные.
Переменная— это имя, которым обозначается какой-либо фрагмент информации. Переменные могут быть использованы для хранения самых разнообразных видов данных, начиная от
баллов, набранных в компьютерной игре, и заканчивая биржевыми показателями.
Имена переменных часто несут в себе какую-то информацию о принимаемых значениях.
Например, по имени n R a d i u s можно догадаться, что переменная используется для хранения
(и представления) радиуса круга. С другой стороны, переменным иногда присваиваются не
очень удачные имена, по которым трудно сделать вывод об их назначении. Например, имя
С4Р0 может ассоциироваться с серийным номером, паролем и вообще с чем угодно.
Каждый раз, когда необходимо получить доступ к хранимой в переменной информации,
ссылаются на имя переменной. Поскольку C++ является строгим языком программирования,
перед тем как приступить к использованию переменной, нужно объявить ее тип данных.
В этой главе рассматриваются вопросы, связанные с именованием переменных, их определением и инициализацией.

Ымеяование переменных
При выборе имени для ребенка родители сталкиваются с множеством ограничений. Допустим, например, что они сами помешаны на программировании и надеются, что, когда их
чадо вырастет, непременно станет великим программистом. Тогда вместо имени Джон они
могли бы дать ему имя \У\У\УДЖОН. ИЛИ вместо имени Джессика назвали бы девочку double.
Но к сожалению, даже если соответствующее административное учреждение (и то не каждое)
зарегистрирует такое имя, у ребенка будут проблемы со сверстниками, тяжелые школьные
годы, и он никогда не простит своих родителей за такой "подарочек".
Выбрать имя для переменной намного проще. Ей безразлично, как вы ее назовете, поэтому смело давайте ей любое понравившееся имя. Правда, и здесь существуют некоторые ограничения. Впрочем, они вполне разумны и их легко запомнить. Итак, имя переменной:
|
|
|
I
!)
|
I

S не должно начинаться с цифры;
S не должно содержать пробелов;
S может состоять только из букв, цифр и символов подчеркивания ( __ ). Нельзя использовать специальные символы, такие как точка, запятая, точка с запятой, кавычки и т.п.;
^ не должно совпадать с названиями библиотечных функций;
^ не должно совпадать с зарезервированными ключевыми словами.

Глава 8. Использование переменных

81

В табл. 8.1 приведен перечень ключевых слов Visual C++, которые являются командами языка
C++. Дочитав книгу до конца, вы будете знать, как пользоваться большинством из эгих ключевых
слов. Слова, начинающиеся с двух символов подчеркивания (
), являются специальным расширением Visual C++, которое призвано упростить процесс создания программ для персональных
компьютеров. Просмотрите таблицу и не присваивайте переменным таких же имен.

Таблица 8.1. Ключевые слова C++
• , и „ 4 - „ „ ~ 4 c D S U
c i - i .

hock

multiple

a

l

i

identifier

nog с

a

s

m

if

exists

no op

a

s

if

not

g

s

n

o

f

n i n e

e

exists

i n l ir.e

b

a

s

d

b

o

x

c

d

o

c

i

d

e

c

l

s

p

e

d

e

l

e

g

a

t

e

v

e

n

t

e

x

c

e

p

t

f

a

s

t

c

a

l

f

i

n

a

l

l

y

t

o

r

e

g

-

e

i

__Pin
property

intS

raise

intl6

sealed

c

in-.32

single inheritance

e

int64

stdcail

interfase

super

leave

try cast

rri6 4

try/

except

ml2 8

try/

finally

ml28d

unhook

l

n

l

i

n

e

ml28i

uuidof

flea:

signed

for

sizeof

friend

static

goto

static cast

if

struct

inline

switch

int

template

long

this

С hi Г

mutable

thread

class

naked

threw

с •. n ? t

namespase

true

new

try

continue

r.cinl i n e

typedef

iefault

r.creturn

typeid

v

a

l

u

e

v

i

r

t

u

w

l

i

M

w

c

b

o

o

l

b

r

e

a

c

a

s

e

c

a

t

c

h

a

r

t

k

h

const

82

a

inheritance

cast

n

h

e

r

i

t

a

n

c

e

Часть II. Все, что вы хотели знать о C++, но о чем боялись спросить

Окончание табл. 8.1

delete
deprecated
dllexport
dllimport
do
double
dynamic_cast
else
enum
explicit
extern
false

nothrow
novtable
operator
private
property
protected
public
register
reinterpret cast
return
selectany
short

typename
union
unsigned
using
uuid
virtual
void
voletile
wchar t
while

Вот примеры допустимых имен переменных: way_cool, RigthOn, B i t s 3 2 . А такие
имена присваивать переменным нельзя: c a s e (совпадает с ключевым словом), 52PickUp
(начинается с цифры), A Louse (содержит пробел), н—v (включает недопустимые символы).
В именах переменных строчные и прописные буквы воспринимаются как разные. Например, имена b a r s , E a r s , bArs и BARS обозначают разные переменные.

Определение nefiejietiitMX
Перед тем как использовать переменную, ее нужно определить. Для этого просто укажите
тип принимаемых значений и ее имя. Вот, например, определение нескольких переменных:
i n t Counter;
double OrNothing;
long J o h n s ;
Если объявляемые переменные имеют один и тот же тип данных, объявить их можно в
одной строке. Например, если нужно объявить переменные F i r s t , Second и T h i r d , каждая
из которых должна принимать значения типа f l o a t , можете сделать это так:
float F i r s t ;
float Second;
float Third;
Или так:
float First, Second, Third;

пе/геменных
Одновременно с объявлением переменные можно инициализировать. Этот громоздкий
термин обозначает всего лишь присвоение переменным исходных значений. Чтобы сделать
это, наберите после имени переменной знак равенства ( = ) и затем укажите нужное значение.

Глава 8. Использование переменных

83

Например:
int Counter = 3;
double OrNothing = 3.5;
long Johns = 32700;
Вот и все. Ничего сложного, не так ли?

/date сделсипь имя ин.фо(и1сипи£н.ым
Вы, конечно, можете присваивать переменным самые разнообразные имена (за небольшими исключениями, они упоминались выше), однако обычно программисты стараются придерживаться некоторых общих соглашений, цель которых — упростить чтение и понимание
кодов программ. Основным из них является так называемое венгерское обозначение, придуманное, как говорят, неким венгром, работающим на компанию Microsoft. Его идея состоит в
том, чтобы начинать имена со специальных префиксов из нескольких букв, обозначающих
тип переменной. В этой книге при назначении имен переменным используется одна из версий
венгерского обозначения, которая, на наш взгляд, является самой простой и удобной.
Например, чтобы обозначить, что переменная имеет тип integer ( i n t ) , начинаем ее имя с
буквы п:
int nRadius;
int nCount - 0;
В табл. 8.2 приведен список префиксов, используемых в книге. Напомним, что, хотя использование таких префиксов является хорошей практикой, позволяющей помимо прочего
допускать меньше ошибок, вы всегда можете отказаться от них и присваивать переменным
такие имена, которые вам больше нравятся.
Таблица 8.2. Использование префиксов
Тип данных

Префикс

Пример

Integer

n

nCount

Double

dbl

dblRadius

String

psz

pszName

Boolean

f

fFinished

object

о

oLiriG

object pointer

po

poCircle

array

a

aShapes

member

m

m nShapes

Некоторые типы данных, упомянутые в этой таблице, вы встречаете, наверное, впервые.
Например, массивы (array) будут описаны в главе 14, а объекты (object) —- в главе 17.

84 Часть II. Все, что вы хотели знать о C++, но о чем боялись спросить

Глава 9

Структуры данных
й гла£е...
> Объявление и использование структур
V- Комбинирование структур
>• Добавление структур к приложению
предыдущей главе рассматривались простые типы данных и способы использования
переменных для хранения информации. Но предположим, что необходимо сохранить
более сложную информацию, которая не может быть представлена только лишь одной переменной какого-то определенного типа данных. Например, информацию о сотруднике, которая включает его имя, адрес и телефонный номер. Если рассматривать только простые типы
данных, вы должны использовать для этой цели три различные переменные: одну для представления имени, вторую для адреса и третью для телефонного номера.
Такой подход нельзя назвать самым удачным, поскольку в реальном мире почти каждое
явление описывается целым набором разных значений. Представим, например, что вам необходимо отобразить информацию о сотруднике. Несмотря на то что эта информация состоит
из разных частей (имя, адрес, телефон и т.п.), вы все равно представляете ее как нечто целое.
По крайней мере намного предпочтительнее и удобнее сказать: "отобрази информацию о сотруднике", чем: "отобрази имя сотрудника, его адрес, телефон и т.д.".
В этой главе описано, как объединить несколько отдельных переменных в нечто целое,
называемое структурой. Группируя связанные по смыслу элементы в общую структуру, вы
значительно упрощаете чтение и понимание кодов программ. Возможно, вы будете использовать структуры во всех своих будущих программах, по крайней мере в этой книге вы будете
часто с ними встречаться. В главе 17 рассматривается, как обычные структуры могут быть
преобразованы в классы—• самые главные компоненты, из которых строятся объектноориентированные программы.

Объявление структур во многом похоже на объявление обычных типов данных (см. главу 7).
Чтобы объявить структуру, наберите слово c l a s s , затем укажите название структуры. Дапее в фигурных скобках наберите p u b l i c : и объявите переменные, из которых будет состоять структура.
Вот пример объявления структуры:
class Circle Info
{
public:
double dblRadius;
double dblArea;

};
В данном случае структура названа именем C i r c l e l n f о (Информация о круге). Она состоит из переменной d b l R a d i u s типа double, в которой хранится значение радиуса крута, и
переменной d b l A r e a (также типа double), в которой хранится значение площади круга.
Чтобы определить переменную, которая будет представлять структуру, просто наберите
название структуры и затем название переменной.

Глава 9. Структуры данных

85

//Создание переменной, представляющей структуру
Circlelnfo oCircle;
Что в действительности означает определение переменной, представляющей структуру? Приведенной выше строкой была создана переменная o C i r c l e типа C i r c l e l n f o . Это означает, что
переменная c C i r c I e содержит две другие переменные — dblRadius и dblArea, поскольку эти
переменные определены для структуры CircleTnfo. Можно сказать, что одна переменная
(oCircle) типа C i r c l e l n f o содержит несколько значений (dblRadius и dblArea).
Объединяя различные переменные в отдельные структуры, можно логически правильно
организовать используемую в программе информацию. Далее вы узнаете, как используются
переменные, представляющие структуру.
Разница между объявлением и определением
В C++ объявление и определение являются техническими терминами. Их смысл несколько различается,
но часто их используют как взаимозаменяемые (хотя по сути это неправильно).
Обьявляя структуру, вы сообщаете компьютеру, из чего зта структура будет состоять. Например:

class Circlelnfo
{
public:
double dblRadius; double dblArea;
• J;
При этом память для структуры не выделяется.
Определяя переменную, вы даете компьютеру указание создать эту переменную. В этом случае для создаваемой переменной выделяется память:

Circlelnfo oCircle;

использование э/них загадочных critfu/jctni//i
После того как структура создана, чтобы получить доступ к хранимой в ней информации (к
значениям переменных, из которых состоит структура), наберите имя переменной, представляющей структуру, затем точку (.) и затем имя переменной, значение которой нужно получить.
Например, чтобы отобразить значение радиуса крута, наберите такой код:
//Создание переменней, представляющей структуру
Circlelnfo oCircle;
//Отображение части информации, хранимой в структуре,
//которая обозначается именем d b l R a d i u s
Console::WriteLir.e(oCircie.dolRadius.ToString());
Как видите, выражение o C i r c l e . d b l R a o i u s выступает здесь в роли обычной переменной. Использование элементов, входящих в структуру, по существу, ничем не отличается от
использования стандартных переменных, представляющих только одно значение.

использование oqnux aftfu/tafUffi
длл созданил
Можно объединить несколько структур для создания одной большой структуры. Структуры, входящие в состав других структур, называются вложенными.

86 Часть II. Все, что вы хотели знать о C++, но о чем боялись спросить

Прежде чем использовать структуру для создания другой структуры, ее нужно объявить.
Например, перед тем как создать структуру MegaCircle, состоящую из структур
C i r c l e l n f o , нужно объявить структуру C i r c l e l n f o . Впрочем, это вполне логично. Нужно просто не запутаться, что и в какой последовательности должно быть объявлено. Если же
вы нарушите естественный порядок. Visual C++ .NET выдаст сообщение об ошибке.
Вот пример объявления структуры, состоящей из двух структур C i r c l e l n f o :
c l a s s MegaCircle
{

public:
Circlelnfo oTopCircle;
Circielnfo oBottoraCircle;

dnfttfiantffibi на п/ии&ишсе
Ниже приведен код программы CircieArea, который был немного изменен в связи с использованием структуры для представления данных.

//CircleArea3
//Вычисление площади по значению радиуса
//Использование структуры для представления данных
#include "stdafx.h"
#using
using namespace System;
//Объявление структуры для представления информации
//о параметрах круга
class Circlelnfo
{
public:
double dblRadius;
double dblArea;
//С этой строки начинается выполнение программы
#ifdef ^UNICODE
int wmain(void)
#else
int main(void)
#endif
{
const double PI = 3.141592;
Circlelnfo oCircle;
//Отображение вопроса о значении радиуса
Console::WriteLine(S"Укажите радиус круга");
//Получение ответа и преобразование его к числу с
//плавающей запятой
Глава 9. Структуры данных

87

oCircle.dblRadius = Double::Parse(Console::ReadLine());
//Вычисление плошади круга
oCircle.dblArea ~ PI*oCircle.dblRadius*oCircle.dblRadius;
//Отображение результата на экране
//Обратите вникание, что для этого число должно
//быть предварительно прообразовано в строку
Console::WriteLine(S"Площадь круга составляет {0}
единиц,", oCircle.dblArea.ToString{));
//Ожидание, пока пользователь не остановит
//выполнение программы
Console::WriteLine(S"Нажмите клавишу Enter, чтобы
остановить выполнение программы");
Console;:ReadLine();
return 0;
}
Вы могли обратить внимание на несколько интересных моментов в этой программе. Так,
структура C i r c l e l n f o была объявлена перед функцией main (это функция, с которой начинается выполнение программы). Именно так и нужно делать, поскольку структура должна
быть объявлена до того, как вы начнете ее использовать. В данном случае функция main использует структуру C i r c l e l n f o . поэтому она и должна быть объявлена перед кодами функшш main. Это общий принцип для всех программ на C++. Сначала объявляется класс. Определяются все его функции. И только после этого класс можно использовать.
Поначалу вам может показаться это несколько странным, поскольку более простые и, по
сути, служебные элементы описываются раньше, чем тс элементы, в которых они используются. Но по мере приобретения опыта программирования на языке C++ вы к этому привыкнете. Пока же для удобства, если хотите, можете приступить к чтению кодов с того места, где
начинается выполнение программы.
Кое-что о структурах и классах
В этой книге при создании структур будет использоваться ключевое слово c l a s s . Это же слово используется и при создании классов. (О них речь идет в главе 17.)
В действительности в языке C++ есть два слова, которые могут быть использованы для объявления
структур (или классов): c l a s s и s t r u c t . Использование слова s t r u c t почти эквивалентно использованию слова с 1 as s, за исключением лишь того, что в этом случае вам не обязательно добавлять слово p u b l i c : перед описанием переменных, входящих в структуру. Некоторые предпочитают использо- ,
вать слово s t r u c t при объявлении структур и слово c l a s s при объявлении классов (которые представляют собой те же структуры, содержащие в себе не только переменные, но и функции). Обратите
внимание, что для объявления классов также можно использовать слово s t r u c t .
Когда вы поближе познакомитесь с классами (начиная с главы 17), вы узнаете, что означает слово
p u b l i c , а также слова p r i v a t e и p r o t e c t e d . Если вы применяете слово s t r u c t , по умолчанию используется слово p u b l i c , а если c l a s s - слово p r i v a t e . Наш совет: чтобы не путаться,
постоянно используйте слово c l a s s при объявлении классов и структур.
Почему два ключевых слова выполняют практически одни и те же функции? Слово s t r u c t использовалось еще в языке С и было оставлено для того, чтобы программы, написанные на языке С, можно было
компилировать с помощью компилятора C++. В языке C++ этому слову добавили немного функциональности, и теперь оно почти идентично слову c l a s s . Но, поскольку вы являетесь программистами на
C++, используйте при написании своих программ только слово c l a s s ,

88 Часть II. Все, что вы хотели знать о C++, но о чем боялись спросить

Глава10

Выразите свои желания
главе...
>
>
>
>
>
V

Чем являются выражения
Знакомство с основными операторами
Логические выражения
Оценка шачений
Сравнение значений
Использование математических функций

*77
им рограммы обрабатывают данные, и одним из видов такой обработки является вы• ^ •" полнение различных вычислений. Указание провести вычисления (которое записывается в виде формулы), в языке Visual C++ .NET называется выражением. Если вы знакомы
с электронными таблицами (например, Excel), то наверняка уже имеете опыт использования
выражений: каждый раз, набирая формулы в ячейках, вы набирали выражения.
Выражения применяются для получения новых данных исходя из уже имеющихся. Например, выражения можно использовать для вычисления площади круга, объема воздушного шара,
веса танка или проведения более сложных вычислений, скажем, подведения итогов голосования.

> b a r

Сдвиг разряда вправо. Выражение f o o >> b a r возвращает тот же
результат, что и выражение f o o / 2 b a r . Более подробно этот
оператор описан ниже в главе

< < bar

Сдвиг разряда влево. Его действие противоположно действию оператора
>>. Более подробную информацию вы найдете далее в главе

90 Часть П. Все, что вы хотели знать о C++, но о чем боялись спросить

Оператор
Этот оператор не так прост, как может вначале показаться, поскольку степень увеличения зависит от типа переменной, значение которой увеличивается. Например,
если у вас есть указатель на переменную f оо и значение переменной foo занимает
четыре байта, то оператор увеличения, примененный к указателю, увеличит его значение на число 4. поскольку именно в этом случае указатель будет ссылаться на
следующее значение после значения переменной foo. Сложновато, не так ли? Более детальную информацию об указателях вы можете найти в главе 13.
Есть два способа использования оператора ++. Его можно набрать или перед именем переменной: ^ +bar, или после: саг++.
Набрав +-+bar, вы даете указание вначале увеличить значение переменной b a r . а затем
его использовать. Рассмотрим уго на таком примере:
i r . t b a r :-- 1 ;
Console::WriteLine(+-bar.ToString());
В этом случае значение переменной b a r будет увеличено на число 1, и на экране будет
отображено число 2 (новое значение переменной bar).
Напротив, если вы наберете bar++, значение этой переменной вначале будет обработано,
а затем увеличено. Например:
i n t bar •-- 1;
cout > 1 возвращает число 8;
16 >> 2 возвращает число 4;
16 >> 3 возвращает число 2;
15 >> 1 возвращает число 7;
15 >> 2 возвращает число 3.
Для получения результата первое число представляется в двоичном виде, а затем все биты
сдвигаются вправо на количество позиций, указанное вторым числом. Обратите внимание,
что при сдвиге битов вправо число уменьшается.
Для внесения ясности рассмотрим действие оператора сдвига подробнее. Число 16, например, в двоичном виде выглядит так:
10 0 0 0

Глава 10. Выразите свои желания

91

Если сдвинуть все биты вправо на одну позицию, получим число
0 1000
В десятичной системе это число соответствует числу 8, поэтому выражение 16 >>
даст число 8.
Рассмотрим другой пример. Число 15 в двоичной системе счисления выглядит так:

1

0 1111
Поэтому результатом операции 15 >> 2 будет число
0 0 0 11
В десятичной системе ему соответствует число 3.

Оператор «
Ниже приведены примеры использования оператора
' А вот тот же код, набранный без отступов:
if (nHandValue > 21)(
'
'
'
//В картах перебор
nUserScore -= nBet; fBusted = true;} else {"
//Игроку предлагается взять еще одну карту
cout « "Еще?\п"; cin >> fHitMe;}
Первый код проще, поскольку из него ясно видно, какие инструкции будут выполнены в случае выполнения условия, а какие в случае невыполнения.
Если вы читаете главы книги по порядку, то уже видели использование этого приема на примере функции
m a i n , все строки которой выделяются отступами. Атакже видели нечто подобное при создании структур.
При использовании вложенных операторов выделяйте коды каждого из них дополнительными отступами.
Например:

Глава 11. Ключевые слова — ключ к диалогу с компьютером

107

if (too)
{
bar-f+
if (bar > 3)
{
baz = 2;
}
if (goober < 7)
{
flibber - 3;
Другим приемом, облегчающим чтение кодов программы (который также использовался во всех приведенных выше примерах), является размещение закрывающей фигурной скобки (}} на одном уровне с соответствующей ей открывающей скобкой:
if

(fFoo)

}

'

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

(fFoo)
nVal++;
nCheck++;

'

,

Приведенный ниже код выполняет точно те же действия, но читается намного проще и позволяет в случае
необходимости без особых усилий добавить к оператору i f дополнительные инструкции для выполнения:
if (fFoo)
{
nVai++;
. nCheck++;
Visuaf C++ .NET форматирует коды вашей программы автоматически. Например, если вы выделите с помощью отступа какую-то строку, набираемые после нее строки будут автоматически выравниваться по
: ней. Если вы наберете скобку }, она автоматически будет выровнена по соответствующей ей скобке {.
Следующий код определяет размер предоставляемой скидки в зависимости от суммы совершенных покупок:
//Если сумма превышает $5000, скидка равна 30%
if (nSumrr, > 5000}
{

dblDiscount = 0.3;
}

else
{
//Если сумма превышает $3000, скидка равна 20%
if (nSumm > 3000)

108 Часть II. Все, что вы хотели знать о C++, но о чем боялись спросить

dblDiscount = 0.2;
}
//В противном случае скидка не предоставляется
else
{
dblDiscount = 0.0;

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

Оператор for
Ключевое слово for используется в тех случаях, когда какие-то инструкции должны выполняться подряд определенное количество раз. Синтаксис оператора for выглядит следующим образом:
for (expl; expr2; ехргЗ)
stmtl;
Такой код еше называют IJUXJIOM for.
При выполнении цикла f o r в первую очередь обрабатывается выражение e x p r l , которым определяется начальное значение переменной, используемой для контроля за количеством итераций. {Итерация — это одноразовое выполнение инструкций цикла.) Затем оценивается выражение ехрг2. (Оно оценивается перед каждой итерацией, для того чтобы определить, следует ли продолжать выполнение цикла.) Если выражение ехрг2 истинно,
выполняется инструкция s t r a t i , после чего обрабатывается выражение ехргЗ. Выражение
ехргЗ используется обычно для изменения значения переменной, контролирующей количество итераций. Если выражение ехрг2 ложно, выполнение цикла прекращается и программа
переходит к обработке следующих за этим циклом кодов.

Пример использования цикла for
Приведенное выше объяснение может показаться вам не очень понятным, поэтому продемонстрируем работу цикла f o r на практическом примере.
int i;
for (i = 0; i < 2; i++)
{
Console::WriteLine(i.ToString());
}

Опишем, как этот код выполняется.
1. Вначале обрабатывается выражение ехрг]. Переменной i присваивается значение 0.
2. Затем обрабатывается выражение ехрг2. Проверяется, меньше ли значение переменной i числа 2. Поскольку переменной i только что было присвоено значение 0, выражение ехрг2 будет истинным, что дает разрешение на выполнение инструкции
s tint 1. В данном случае эта инструкция выглядит так:
Console::WriteLine(i.ToString()) ;
Ее выполнение приводит к отображению на экране значения переменной i.

Глава 11. Ключевые слова — ключ к диалогу с компьютером

109

3. Далее обрабатывается выражение ехргЗ. В данном случае оно выглядит как i++, таким образом, значение переменной i увеличивается на единицу.
4. Поскольку выражение ехрг2 должно оцениваться перед выполнением каждой итерации, программа снова возвращается к нему. Переменная i имеет уже значение 1, но это
все равно меньше числа 2. Поэтому инструкция s t r a t i выполняется еще раз, и на экране отображается следующее значение переменной i.
5. Снова обрабатывается выражение ехргЗ. Значение переменной i увеличивается на
единицу и становиться равным числу 2.
6. Программа переходит к проверке выражения ехрг2. Теперь оно ложно, поскольку значение переменной i (2) не меньше числа 2. На этом выполнение цикла заканчивается.
Изменяя и усложняя выражение ехрг2, можно задавать самые разные условия, определяющие количество выполняемых итераций и момент прекращения выполнения никла. Далее
в примерах этой книги вы еще неоднократно встретитесь с ним.

Повторение ради повторения
Если вы хотите, чтобы инструкция была выполнена несколько раз подряд, наберите такой код:
for (i = 0; i < n; i++)
{

//Инструкция или набор инструкций для повторения
}
Значение п определяет количество выполняемых итераций. Например, если вам нужно,
чтобы строка "Терпение и еще раз терпение" была выведена на экран 50 раз подряд, наберите
такой код;
for ( i = 0; i < 50;i++)
{

Console: :WriteLine (S''Tepn-ение и еще раз терпение");
)
Чтобы не показаться навязчивым, можете позволить пользователю самому определить,
сколько раз строка должна отобразиться на экране. Делается это так:
i n t nCount;
Console::WriteLine(S"Сколько строк отобразить?");
nCour.t = Console: : ReadLine () ->ToInt32 {);

for (i = 0; i < nCount;
{
Console: :WriteLine (3"Терпение ~л еще раз терпение") ;
}

Вычисление факториала
Наверное, каждый программист когда-нибудь сталкивался с такой проблемой: как написать код, вычисляющий п-факториал?
Это выражение (п-факториал) вычисляется по такой формуле: пх (п - 1) х (п - 2) х ... х 1.
Так, 2-факториал (пишется как 2! в математических книгах, но не в компьютерных кодах)
вычисляется как 2 х 1, а 3! ~ как 3 х 2 х 1.
Одним из вариантов решения такой проблемы может быть приведенный ниже код.
//Вычисление г.!
р. = I n t 3 2 : : P a r s e (Console : : ReadLine () ) ;

110 Часть //. Все, что вы хотели знать о C++, но о чем боялись спросить

if (n == 1)
Console::WriteLine(S"l" ) ;
}
else if fn == 2)
{
Console::WriteLine(S"2" ) ;
}
else if (n == 3)
{
Console::WriteLine(S"6") ;
}
Этот способ, несмотря на свою простоту, крайне неэффективен, поскольку, даже если вы
будете продолжать аналогичным образом набирать коды для чисел 4, 5, ... 35 и т.д., все равно
вы не сможете перебрать даже малую часть всех возможных вариантов. Реальным решением
этой проблемы может быть использование цикла f o r .

//Factorial
//Вычисление п!
#include "stdafx.h"
#using
using namespace System;
//С этой строки начинается выполнение программы
#ifdef JJNICODE
int wmain(void)
#eise
int main(void)
#endif
{
int nNumber; //Число, определяемое пользователем
int nResult = 1;
int i;

//Переменная, используемая для отсчета
//количества итераций
//Получение числа, для которого нужно вычислить
//факториал
Console::WrileLine("Для какого числа?");
nNumber = Int32: :Parse(Console::ReadLine()) ;
//Далее вычисляется факториал путем умножения на
//каждой итерации значения переменной i на
//общий результат. Таким образом мы получим число,
//равное 1*2*3*...*п
for (i=l; i'i"B действительности указатели— это один из самых полезных и мощных инструментов,
используемых при создании программ, поскольку они обладают большой гибкостью и предоставляют разработчикам большую свободу действий.

Что вы! Указатели — это очень сложно
У неопытных программистов использование указателей вызывает сложности по двум
причинам. Во-первых, указатели предоставляют доступ к двум различным фрагментам информации. Во-вторых, указатели могут ссылаться на данные, которые не имеют имени.

Информация и ее адрес
Как только что было отмечено, указатели предоставляют доступ к двум различным фрагментам информации.
/

Значение, сохраненное самим указателем. Этим значением всегда будет адрес в
памяти, где хранится другая информация. Например, если указатель содержит значение 4, это значит, что он указывает на адрес 4.

V Значение, на которое указывает указатель. Например, если в памяти под адресом 4 хранится значение 17, указатель, содержащий значение 4, указывает на значение 17.
Значение, которое содержится в указателе, является просто адресом в памяти компьютера.
Если вывести на экран значение указателя, вы увидите только число, являющееся адресом.
(Поскольку само по себе это число не несет никакой смысловой нагрузки, на экран оно почти
никогда не выводится.) Но указатель может также предоставить доступ и к более полезной
информации — к значению, которое хранится под тем адресом, на который он указывает.
Определение значения, на которое ссылается указатель, называется его разъшеновывтшем.
Например, указатель b a z содержит значение 4 (см. рис. 13.1). Если вы разыменуете его,
то получите значение 17, поскольку именно оно хранится в адресе 4.
Звучит слишком абстрактно? На самом деле в этом нет ничего сложного, ведь даже в повседневной жизни мы сталкиваемся с разыменовыванием. Например, когда вы набираете номер телефона, автоматически вызов направляется именно к тому абоненту, с которым вы хотите поговорить. Другими словами, номера телефонов в вашей записной книге являются указателями на самых разных людей. И когда вы разыменовываете номер телефона (т.е. просто
набираете его), вы получаете доступ к нужному человеку.

Безымянные данные
Вторая причина, вызывающая трудности при работе с указателями, заключается в том,
что они могут указывать на данные, которые никак не обозначаются (т.е. не имеют никаких
имен). На рис. 13.1 вы видели, как используются указатели для получения доступа к значениям различных переменных. Так, например, указатель b a z ссылался на значение переменной
b a r . Следовательно, вы могли разыменовать указатель b a z , чтобы получить значение переменной bar. В данном случае указатель ссылается на участок памяти, который обозначен определенным именем (этим именем является имя переменной, т.е. b a r ) .
Вы можете также попросить компьютер сохранить какую-то часть информации в памяти
без присвоения этой информации имени. К этой возможности очень часто обращаются в случае, если возникает необходимость в динамическом использовании памяти. В указателе можно сохранить адрес информации, не обозначенной именем, и затем использовать его для доступа к этим данным.

Глава 13. Указатели

131

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

; Если вы знаете адреса этой памяти, то можете получить к ней доступ с помощью указателей и как-то изменить хранящиеся там данные. Иногда это может быть очень полезной возможностью. Например,
; большинство программ с видеоиграми знают точные адреса памяти, где хранится информация, отображаемая на экране. Благодаря этому они могут в режиме реального времени показывать видеоизображения и сопровождать их специальными эффектами.
Но, с другой стороны, если вы заполните эту память, отведенную для специальных данных, разным информационным мусором, это может привести к возникновению всяких неожиданных проблем. Это состояние называют трещингом компьютера (от слова trash - засорять), поскольку в действительности
специальная память засоряется ненужными данными. Последствия могут быть самыми разными, но в
любом случае нежелательными.
•• Windows пытается отслеживать корректность выполняемых программами действий. Она обычно может
• отличить "хорошие" указатели от "плохих" и останавливает выполнение программы до того, как она успеет сделать какую-нибудь глупость. Более подробно эти вопросы рассматриваются ниже в главе.
Испугались? То-то. Будьте аккуратны при использовании указателей.

Связанный список — размер не ограничен
В этом разделе рассматривается пример, в котором доступ к фрагменту памяти будет
осуществляться с помощью указателя. Предположим, нужно создать программу, сохраняющую информацию о линиях, которые должны быть нарисованы на экране. Каждый раз, когда
пользователь вводит новые координаты, динамически выделяется новая память для их хранения. И затем остается только сохранить адрес этой памяти с помощью указателя.
При этом применяются некоторые маленькие хитрости. В момент создания программы
точно не известно, сколько линий захочет нарисовать пользователь. Но сохранить нужно будет информацию обо всех линиях, сколько бы их ни было. Можно, конечно, объявить целое
множество указателей (Линия!, Линия2, ЛинияЗ и т.д.) и затем использовать их один за
другим. Это не самая удачная идея, поскольку количество объявленных переменных априори
должно быть больще количества линий, которые может нарисовать пользователь (что, если
он захочет нарисовать, например, 20 000 линий). А кроме того, придется использовать гигантский оператор s w i t c h , чтобы определить, какой указатель должен быть использован
следующим.
Намного разумнее и удобнее использовать нечто, называемое связанным списком. Это
список элементов, в котором первый элемент указывает на втррой, второй на третий и т.д.
Это похоже на поезд: первый выгон тянет за собой второй и т.д.
Чтобы сохранить информацию о целом множестве линий также можно использовать связанный список. Каждый элемент этого списка будет хранить информацию о координатах линии, а также указатель на следующую линию. На рис. 13.2 показан связанный список, состоящий из трех элементов. Каждый элемент содержит данные о своей линии, а также указатель на следующий элемент.
Связанные списки очень удобны и часто используются для сохранения данных об элементах, количество или размеры которых заранее не известны.

132 Часть II. Все, что вы хотели знать о C++, но о чем боялись спросить

Line Pointer
Next item Pointer

•*- Linei

Line Pointer
Next Hem Pointer

-» Line 2

Line Pointer
Next Item Pointer

•*- Line 3

Рис. 13.2. Указатели могут указывать на большие и сложные структуры данных. Элементы этого связанного списка состоят из двух указателей; один указывает на информацию
о координатах линии, а второй - на следующий элемент списка

использование цказайьелей в С+ +
Указатель, ссылающийся на значения определенного типа данных, создается точно так
же, как и переменная того же типа, за исключением того, что перед именем указателя нужно
набрать звездочку (*).
Например, чтобы создать указатель, ссылающийся на значения типа integer, наберите такой код:

int
Если вы хотите придерживаться соглашений о присвоении имен переменным и
указателям, принятым в данной книге, начинайте имена указателей с буквы р
(pointer— указатель), затем набирайте префикс, который соответствует тому типу данных, на значения которого указатель будет ссылаться, и далее указывайте
само имя. Например, указатель, ссылающийся на значения типа integer, должен
начинаться с префикса рп, на значения типа double — с префикса p d b l и т.д.
Объявлять тип данных значений, на которые ссылается указатель, необходимо. Этим
обеспечивается надежность и безопасность создаваемой программы, поскольку в этом случае
компилятор может проверить, не будет ли указатель по ошибке ссылаться на данные не того
типа. Ведь указатель — это всего лишь адрес в памяти компьютера. Если вы определяете для
него тип данных (скажем, integer), а затем по какой-то причине пытаетесь, например, в память, на которую ссылается этот указатель, записать текстовое значение, компилятор выдаст
значение об ошибке. (Это очень хорошая услуга, поскольку именно такие ошибки могут повлечь за собой крайне нежелательные последствия.)

Глава 13. Указатели

133

Если вы набрали i n t *pnFoo, то это не значит, что название указателя —
*pnFoo. На самом деле он называется pnFoo, а звездочка не является частью его
имени и лишь сообщает компилятору, что это указатель.

Дайте указателю адрес
Если вы только что объявили указатель, он еще ни на что не ссылается. Пока это указатель в никуда. И в таком состоянии его лучше не оставлять. Прежде чем использовать указатель, ему нужно присвоить значение (это значение будет адресом в памяти компьютера, и таким образом указатель начнет на что-нибудь ссылаться).
Довольно часто указатели используются для получения доступа к данным, которые являются значениями переменных. Другими словами, указатели содержат в себе адреса переменных, используемых в той же программе. Когда вы разыменовываете такой указатель, вы получаете значение какой-то переменной.
Чтобы получить адрес переменной, наберите амперсант (&) и ее имя. Например, можете
набрать такой код:
//Создание у к а з а т е л я на значение типа i n t e g e r
int *pnPosition;
//Создание переменной типа i n t e g e r
i n t n X P o s i t i o n = 3;
//Присвоение указателю адреса переменной n X P o s i t i o n
p n P o s i t i o n = &nXPosition;
Здесь создается указатель p n P o s i t i o n , который может ссылаться на значения типа
integer, и ему присваивается адрес переменной nXPosition. Если вы разыменуете этот указатель, то получите значение переменной n X P o s i t i o n .
Указатель может указывать на значения только того типа, которые для него объявлены. Так, если для указателя был объявлен тип integer, он может ссылаться
только на переменные типа integer.

Как получить значение, на которое ссылается указатель
Разыменовывание указателя (т.е. получение значения, на которое он ссылается) выполняется очень просто: нужно только перед его именем набрать звездочку (*). Например, чтобы
получить значение, на которое ссылается указатель p n P o s i t i o n , наберите такой код:
//Отображение значения на экране
Console::WriteLine((*pnPosition).ToString());
В результате выполнения этого кода на экране отображается значение, полученное при
разыменовывании указателя p n P o s i t i o n . Этот указатель содержит адрес переменной
n X P o s i t i o n , ее значение равно числу 3, которое и будет отображено на экране.

Пример программы, использующей указатели
А теперь рассмотрим простенькую программу, использующую указатели. (Не огорчайтесь: скоро мы перейдем к более сложным.) В программе объявлена переменная типа integer
и указатель, ссылающийся на значение этой переменной. Вот как указателю присваивается
адрес этой переменной:
pnNumber = &nNumber;
По ходу выполнения программы пользователь набирает число, которое сохраняется как
значение переменной. Затем это значение отображается на экране с помощью кода
nNumber.ToString()

134 Часть //. Все, что вы хотели знать о C++, но о чем боялись спросить

Далее;это Ш значение отображается еще раз, но теперь доступ к нему осуществляется посредством разыменовывания указателя pnNumber:
(*pnNumber).ToString()
Каждый раз, когда вы вызываете функцию (например, T o S t r i n g ) для значения,
получаемого в результате разыменовывания указателя, всегда берите этот указатель в круглые скобки.
//Point
//Объявление к разыменовывание указателя
#include "stdafx.h"
#using
using manespace System;
//Ожидание, пока пользователь не остановит
//выполнение программы
void Har.gOut ()
{
Console::WriteLine(3"Нажмите клавишу Enter, чтобы
остановить выполнение программы");
Conso]e::ReadLine();
//С этой точки начинается выполнение программы
#ifdef _UNICODE
int wmain (void)
#else
int mair. (void)
#endif
{
int *pnNumber;
int nNumber;
//Присвоение указателю адреса переменной nNumber
pnNumber = SnNumber;
//Получение числа от пользователя
Console::WriteLine(S"Укажите число");
nNumber = Int32::Parse(Console::ReadLine());
//Отображение полученного числа
Console::WriteLine(S"Введенное число {0}",
nNumber.ToString());
//Отображение числа с помощью указателя
Console::WriteLine(5"Введенное число {0}",
(*pnNumber).ToString()
• HangOut();
r e t u r n 0;

Глава 13. Указатели

135

Изменение значения, на которое ссылается указатель
Можно не только просматривать значения, на которые ссылается указатель, но и изменять
их. Другими словами, можно не только считывать данные из памяти, но и записывать в память новые данные.
Для этого, как и прежде, нужно лишь использовать звездочку (*). Допустим, например,
что указатель pnNumber ссылается на значение типа integer (как в предыдущем примере).
Чтобы изменить это значение, наберите такой код:
*pnNumber = 5;

Изменение значений в структурах данных
Если указатель ссылается на структуру, можно изменить отдельный элемент этой структуры.
В приведенном ниже примере MyStruct — структура, a poFoo — указатель, который на эту
структуру ссылается. Это значит, что для получения доступа к данным, которые в этой структуре хранятся, можно использовать код *poFoo. Например, можно набрать код наподобие
(*poFoo).value = 7.6;
Продемонстрируем это на таком примере:
c l a s s MyStruct
<
public:
int nData;
double dblValue;
}
//Создание указателя, ссылающегося на такие структуры
MyStruct *poFoo;
//Создание самой структуры
MyStruct oRecordl;
//Присвоение указателю адреса этой структуры
poFoo = &oRecordl;
//Присвоение значения элементу структуры
CpoFoo).dblValue = 7.6;

Использование стрелки
Поскольку набирать код (^указатель) .элемент не очень удобно, C++ предлагает упрощенный вариант:
//Изменение значения элемента структуры
poFoo->dblValue = 7 . 6 ;
Код указатель->элемент можно встретить почти во всех программах C++, использующих указатели.

Динамическое выделение памяти
Каждый раз, когда требуется обработать элементы, количество или размеры которых заранее не известны, вы сталкиваетесь с необходимостью динамического распределения памяти.
Об этом уже упоминалось, когда рассматривался пример программы, обрабатывающей
задаваемые пользователем координаты линий. Количество линий, которые захочет отобразить пользователь, заранее не известно, поэтому при указании координат очередной линии
сразу же выделялась память для объекта, представляющего следующую линию.

136 Часть //. Все, что вы хотели знать о C++, но о чем боялись спросить

Динамическое выделение памяти происходит благодаря использованию ключевого слова
new. Необходимо только сообщить, для данных какого типа нужно выделить память, и оператор new вернет ссылку на участок памяти этого типа. Если в процессе выполнения программы необходимо выделить память для значения типа integer, наберите такой код:
//Создание указателя на значение типа i n t e g e r
int *pn?osition;
//Выделение памяти для значения типа i n t e g e r и
//присвоение этого адреса указателю p n P o s i t i o n
p n P o s i t i o n = new i n t ;
Если нужно выделить память для структуры P o i n t L i s t , наберите
//Создание указателя на структуру P o i n t L i s t
P o i n t L i s t *poPoint
//Выделение памяти для структуры P o i n t L i s t и
//присвоение этого адреса указателю poPoint
p o P o i n t = new P o i n t L i s t ;
Когда вы используете оператор new для создания элемента, указатель запоминает только
адрес, указывающий на выделенный фрагмент памяти. При этом следует быть очень внимательным, чтобы по ошибке не потерять необходимый адрес, на который ссылается указатель,
так как если вы сохраните новый адрес, прежний восстановить будет уже невозможно. Продемонстрируем это на таком примере:
//Забывчивая программа
//Создание указателя типа i n t e g e r
int *pnPosition;
//Выделение памяти для значения типа i n t e g e r
p n P o s i t i o n = new i n t ;
//Сохранение значения по этому адресу
•*pnPosition = 3;
//Выделение памяти для нового значения
p n P o s i t i o n = new i n t ;
Последней строкой этого кода выделяется память для нового значения типа integer. Адрес этого
нового участка памяти будет сохранен в указателе p n P o s i t i o n . Но что случится со значением 3,
сохраненным по адресу, на который до этого ссылался указатель p n P o s i t i o n ? Это значение попрежнему будет храниться в памяти, но, поскольку его адрес потерян (указатель уже ссылается на
совсем другой участок памяти), получить к нему доступ не будет никакой возможности.
Если вы по какой-либо причине потеряете адрес, который для каких-то данных был выделен
динамически, эти данные останутся на своем месте, но найти их уже будет невозможно. Это явление называется утечкой памяти. Потерянные данные так и будут занимать память, и вы не сможете их удалить до тех пор, пока программа не завершит работу. (В среде .NET есть множество возможностей, позволяющих справиться с этой проблемой, о чем речь пойдет несколько ниже.)
Перед тем как использовать оператор new, убедитесь, что данные, адрес которых пока
еще хранится в указателе, более не нужны. Кроме того, если вы хотите создать действительно
качественную программу, перед использованием оператора new освободите память от тех
данных, которые вам больше не нужны, с помощью оператора d e l e t e (более подробно об
операторе d e l e t e речь идет ниже в главе).

/Зниимшие — графика
Теперь, когда у вас есть общее представление об указателях, можно приступить к созданию
более интересных программ .NET. В этом разделе коснемся графических возможностей среды
.NET. Так же как до этого вы использовали функции C o n s o l e для считывания и отображения
текста, теперь вы будете использовать функции G r a p h i c s для отображения на экране графики.

Глава 13. Указатели

137

Но, прежде чем отобразить что-то на экране, нужно создать графический объект. Делается это с помощью кода, подобного приведенному ниже,
Form *poForm = new Form О;
Graphics *poGraphics = poForm->CreateGraphics();
Сплошные указатели! Когда вы хотите что-то нарисовать, сделать это можно в окне (не в
том, из которого вы смотрите на улицу, а в окне, похожем на те, что используются в среде
Windows). Один из самых простых способов сделать это— использовать класс Form. Затем
можно вызвать процедуру CreateGraphi.es, для того чтобы создать графический элемент,
называемый объектом G r a p h i c s . Обратите внимание, что для доступа к объектам Form и
G r a p h i c s используются указатели, а для вызова методов этих объектов — символы ->.
Перед тем как что-то нарисовать, нужно сделать форму видимой с помощью кода, подобного этому:
poForm->Show();
Затем можно вызывать графические функции .NET, часть из которых показана в
табл. 13,1. Нарисовав все, что нужно, вызовите метод Dispose:
poGraphics->Dispose() ;
Таблица 13.1. Некоторые графические команды
Метод

Пример

Описание

Clear

poG->Clear(Color::Red}

Заполняет весь графический элемент
определенным цветом (см, также табл. 13.2)

Dispose

poG->Dispose

Освобождает память, занятую информацией о
графических объектах, Метод вызывается
после завершения этапа рисования

DrawEllipse

poG->DrawEllipse
(poPen, 10, 20, 150,
200)

Рисует эллипс, используя указанное перо.
(Перья описываются несколько ниже.)
Числовые параметры - это координаты х и у,
которые определяют верхний левый угол
эллипса, а также значения ширина и в ы с о т а . В действительности х, у, ширина и
в ы с о т а определяют положение и размеры
прямоугольника, внутри которого будет
нарисован эллипс. Если высоту и ширину
сделать равными, получится круг

DrawLine

poG->DrawLine(poPen,
10, 20, 160, 220)

Рисует прямую линию, соединяющую две точки.
В данном случае линия будет соединять точку с
координатами (10,20) и точку с координатами
(160,220)

DrawReсtangle poG->DrawRectangle
(poPen, 10, 20, 150,
200)

Рисует прямоугольник. Два первых числовых
параметра определяют верхний левый угол
прямоугольника, два следующих - его ширину и
высоту

DrawString

Рисует текст указанными шрифтом, кистью и
начиная с указанной позиции, Шрифты и
кисти будут описаны ниже

138

poG->DrawString
fS"Test", poFont,
poBrush, 10, 20)

Часть П. Все, что вы хотели знать о C++, но о чем боялись спроси

Окончание табл. 13.1
Метод

Пример

Описание

poG->FillEllipse

Рисует эллипс, закрашенный указанной

poG->FillRectangle
(poBrush, 10, 20, 150,
200)

Рисует прямоугольник, закрашенный
указанной кистью

(poBrush, 10, 20, 150,
200)

FillRectangle

кистью

Все цвета радуги
Для каждого графического элемента необходимо указывать цвет, которым он будет нарисован. Осуществляется это с помощью структуры C o l o r . Всего возможны два варианта: либо исполыовать один из заранее заданных цветов, либо создать свой цвет, вызвав метод
FromArgb.
В табл. 13.2 показан список предопределенных цветов. Единственное предостережение: не
принимайте пищу во время отображения объектов с такими цветами, как PapayaWhip или
LemonChi f f on. Иначе вы рискуете испортить себе аппетит и вызвать расстройство желудка.

Таблица 13.2. Предопределенные цвета
AliceBlue

DarkOrange

Khaki

AntiqueKhite

DarkOrchid

Lavender

Aqua

DarkRed

LavenderBlush

Aquamarine

DarkSalmon

LawnGreen

Azure

DarkSeaGreen

LemonChiffon

Beige

DarkSlyteBlue

LightBlue

Bisque

DarkSlyteGray

LightCoral

Black

DarkTurquoise

LightCyan

BlanchedAlmond

DarkViolet

LightGoldenrodYellow

Blue

DeepPink

LightGray

BlueViolet

DeepSkyBlue

LightGreen

Brown

DimGray

LightPink

BurlyWood

DodgerBlue

LightSalmon

CadetBlue

Firebrick

Light5eaGreen

Chartreuse

FloralWhite

LightSlateGray

Chocolate

ForestGreen

LightSteelBlue

Coral

Fuchsia

LightYellow

Cornflower

Gainsboro

Lime

Олава 13. Указатели

139

Окончание табл. 13.
Cornsilk
Crimson
Cyan
DarkBlue
DarkCyan

GhostWhite
Gold

LimeGreen

Goldenrod
Gray

Magenta

Green

MediumAquamarine

DarkGoldenrod

GreenYellow

MediumBlue

DarkGray

Honeydew

MediumOrchid

DarkGren

Hotpink

MediumPurple

DarkKhaki

MediumSeaGreen

DarkMagenta

IndianRed
Indigo

DarkOliveGreen

Ivory

MediumSpringGreen

MediumTurquoise

PaleVioletRed

SlateBlue

MediurnVioletRed

PapayaWhip

SlateGray

MidnightBlue
MintCream

PeachPuff
Peru

Snow

MistyRose

Pink

SteelBlue

Moccasin

Plum

Tan

NavajcWhite

PowderBlue

Teal

Navy

Purple

Thistle

OldLace
Olive

Red

Tomato

RosyBrown

Transparent

OliveDrab

RoyalBlue

Turquoise

Orange

SaddleBrown
Salmon

Violet
Wheat

SandyBrownSeaShe11

White

Sienna
Silver

WhiteSmoke

SkyBlue

YellowGreen

OrangeRed
Orchid
PaleGoldenrod
PaleGreen
PaleTurquoise

Linen
Maroon

MediumslateBlue

SpringGreen

Yellow

Метод FromArgb получил свое название от слов Alpha-Red-Green-Blue (альфа-красныйзеленый-голубой). Любой другой цвет из диапазона, воспринимаемого человеческим глазом.
может быть получен путем смешивания в разных пропорциях этих трех основных цветов.

140

Часть //. Все, что вы хотели знать о C++, но о чем боялись

Количественное вхождение основного цвета в состав производного измеряется в пределах от
О (означает полное отсутствие) до 255 (означает максимальную интенсивность). Например,
если взять красного 255 единиц, зеленого 0 и голубого 0, то получим ярко-красный цвет. Если
красного взять 128, зеленого 128 и голубого 128, получим светло-серый цвет. Я бы с удовольствием рассказал о всех возможных цветах и оттенках, которые можно получить, комбинируя
основные цвета, но тогда ни на что другое в книге не хватило бы места. (Ради интереса попробуйте посчитать, сколько разных цветов можно получить с помощью этой системы.)
Если вы хотите точно узнать, в каких пропорциях входят основные цвета в состав
нужного вам цвета, откройте какую-нибудь графическую программу, например
Adobe Photoshop. В окне цветоподборщика (color picker) найдите нужный вам
цвет. В этом же окне будут показаны параметры выбранного цвета.
Чтобы использовать голубой цвет, наберите
Color::31ue
Если же вам нужен какой-то специфический цвет, наберите код наподобие этого:
C o l o r : :FormArgb(255, 12 8, 0) ;
Пример использования структуры Color будет показан несколько ниже в этой главе.
В приведенном выше примере указаны значения для красного, зеленого и голубого цветов. Но метод FromArgb может принимать значение еще одного параметр а — альфа, которым обозначается прозрачность графического элемента. Поэкспериментируйте с этой возможностью — быть может, она вам пригодится.
При использовании объекта Color не нужно набирать оператор new для создания нового объекта (как это делается в случае с объектом Graphics). Вместо
этого наберите символы : :, чтобы указать один из предустановленных цветов
или вызвать метод FromArgb.

Перья для рисования
Рисуя линию (метод
DrawLine) или фигуру (методы
DrawEllipse
и
DrawRec'angle), вы используете объект Реп (перо). Этот объект переносит в мир компьютерной графики свойства обычного пера или карандаша. Он рисует линии определенного
цвета и определенной толщины. Чтобы создать красный карандаш, который будет рисовать
линию толщиной в один пиксель, наберите такой код:
poPen = new P e n ( C o l o r : : R e d ] ;
А этим кодом создается синий карандаш с толщиной следа в пять пикселей:
poPen = new Pen(Color::Blue, 5 ) ;
Цвет и толщину объекта Реп можно изменять в процессе выполнения программы. Это
делается путем изменения значений свойств Color и Width этого объекта. Например:
poPen->Wigth = 12;
poPen->Color = C o ] o r : : S a d d l e B r o w n ;
Графические функции среды .NET иногда обозначаются как GDI-K

Глава 13. Указатели

141

Кисточка для раскраски
Если вы хотите нарисовать заполненную чем-то фигуру (используя, например, методы
F i l l E l l i p s e или F i l l R e c t a n g l e ) , вместо объекта Реп используется объект Brush
(кисточка). Эффект применения этого объекта подобен эффекту использования обычной кисточки для рисования. В электронном варианте есть два вида кисточек: S o l i d B r u s h и
T e x t u r e B r u s h . Кисточка S o l i d B r u s h заполняет фигуру сплошным цветом, в то время как
T e x t u r e B r u s h может заполнить фигуру каким-нибудь рисунком или изображением.
Чтобы создать новую кисточку, наберите такой код:
poBrush = new S o l i d B r u s h ( C o l o r : : R e d ) ;
Для изменения цвета кисточки измените свойство Color этого объекта:
poBrush->Color = C o l o r : : T h i s t l e ;

Шрифты
Когда вы применяете метод D r a w s t r i n g для отображения текста на экране, необходимо
выбрать используемый шрифт. Для этого наберите код наподобие
p o F o n t = new F o n t ( " A r i a l " ,

12);

Значением первого параметра определяется сам шрифт, а второго— его размер. Вообще
сушествует множество различных шрифтов. Примеры некоторых из них показаны на рис. 13.3.

Шрифт

Пример

Arial

Hello 123

Courier New

Hello 123

Times New Roman Hello 123
Symbol

HEUO 123

Verdana

Hello 123

WingDings
Рис. 13.3. Наиболее часто используемые шрифты
Можно использовать вспомогательную программу Character Map, чтобы увидеть,
как одни и те же буквы отображаются в разных шрифтах. Это очень полезная
возможность, особенно когда речь идет о шрифтах Symbol и WingDings. Если вам
скучно и вы не знаете, чем заняться, можете составить с помощью этих шрифтов
многозначительное послание, состоящее из разных значков и символов, отправить его своим друзьям и посмотреть, как они на него отреагируют.

Займемся рисованием
Теперь, когда вы познакомились с основами создания графических элементов в среде
.NET, можно приступать к использованию этих возможностей на практике. Ниже приведен
код небольшой программы, которая рисует на экране линии и некоторые геометрические фигуры. При этом используются объекты, представляющие перья, кисти и шрифты.

142

Часть II. Все, что вы хотели знать о C++, но о чем боялись спросит

//Graphics
//Демонстрация графических возможностей .NET
#include "stdafx.h"
#using
#using
#using
fusing
using manespace System;
using manespace System::Drawing;
using manespace System::Windows::Forms;
//С этой точки начинается выполнение программы
tifdef _UNICODE
int wmain(void)
#else
int main(void)
#endif
{
//Создание структур для отображения графики
Form *poForm = new Form();
Graphics *poGraphics = poForm->CreateGraphics();
//Создание красного пера
Pen *poPen = new Pen(Color::Red);
//Отображение окна для рисования
poForrr.->Show () ;
//Отображение линии
poGraphics->DrawLine{poPen, 10, 10, 120, 150);
//Изменение свойств пера и
//отображение новой линии
poPen->Color = Color::FormArgb(100, 255, 200);
poPen->Wigth = 5;
poGraphics->DrawLine(poPen, 120, 150, 10, 150);
//Отображение эллипса с использованием того же пера
poGraphics->DrawEllipse(poPen, 10, 150, 40, 60) ;
//Создание новой кисти
SolidErush *poBrush = new SolidBrush(Color::Tomato);
//Отображение прямоугольника, закрашенного этой кистью
poGraphics->FillRectangle(poBrush, 50, 150, 40, 60);
//Создание шрифта
Font *poFont = new Font("Helvetica", 22);
//Отображение текста этим шрифтом
poGraphics->DrawString(S"Hello", poFont, poBrush, 200, 200);
//Освобождение памяти

£лава 13. Указатели

143

poGraphics->Dispose() ;
//Ожидание, пока пользователь не закроет окно с графикой
Application::Run(poForm);
}
В этой программе необходимо обратить внимание на следующее. Во-первых, на наличие
нескольких новых строк с ключевыми словами t u s i r . g и u s i n g namespace в начале программы. Они нужны потому, что для использования графики и форм в среде .NET требуется
подключение некоторых дополнительных файлов.
Во-вторых, на использование кода A p p l i c a t i o n : :Run в конце программы. Каждый
раз, когда вы используете форму, необходимо набирать код, указывающий системе, что не
нужно прекращать выполнение программы до тех пор. пока пользователь не закроет окно
этой формы. Это намного удобнее применения функции HangOut, которая постоянно использовалась в предыдущих примерах. Форма отображается на экране, пользователь просматривает все, что на ней нарисовано, а когда щелкает на кнопке закрытия формы, программа прекращает работу.

списки и графика
После того как вы немного поупражнялись с указателями (вся предыдущая программа
была построена с их использованием), перейдем к рассмотрению более типичных способов
их применения. В этом разделе внимание будет уделено вопросу создания с помощью указателей связанных списков. Рассмотрено это будет на таком примере: пользователь набирает
координаты произвольного количества точек, для хранения которых используется связанный
список. Когда он закончит, эти точки соединяются между собой с помощью графических команд среды .NET.

Как эта программа работает
В программе создается связанный список. Каждый элемент этого списка представляет собой структуру, состоящую из координат точки, а также из указателя, который ссылается на
следующий элемент этого списка. Указатель последнего элемента списка имеет значение,
равное нулю. Указатели с таким значениями называются пустыми (или нулевыми).
Итак, элемент связанного списка должен содержать в себе значения координат точки и
указатель на следующий элемент. Ниже показан код, которым объявляется структура
P o i n t L i s t , содержащая в себе два значения типа integer и указатель на такую же структуру.
class PointList
{
public:
int nX;
ir.t nY;
PointList *poNext;
};
Программа должна содержать в себе коды, необходимые для добавления в список новых
элементов, и коды, позволяющие находить нужную информацию в этом списке. Для этого
нужны три дополнительных указателя. Первый будет ссылаться на первый элемент списка;
таким образом, вы всегда будете знать, где список начинается. Это будет как бы точкой отсчета, дающей ключ ко всем остальным элементам списка:
P o i n t L i s t * p o P o i n t s = С;

144 Часть II. Все, что вы хотели знать о C++, но о чем боялись спросить

Второй указатель будет каждый раз ссылаться на новый элемент, который добавляется к
списку:
P o i n t L i s t *poNew;
Третий указатель будет содержать ссылку на последний элемент списка:
P o i n t L i s t *poLast;
Он также потребуется при добавлении в список нового элемента, так как последний элемент
списка (теперь уже бывший последний) должен ссылаться на вновь добавляемый элемент.
Теперь рассмотрим, как связанный список будет использоваться для хранения вводимой
пользователем информации. Вначале выделяется память для нового элемента списка:
poNew = new P o i n t L i s t ;
Если это будет первый элемент списка, указатель p o P o i n t s должен ссылаться именно на
него. (Помните, что p o P o i n t s всегда указывает только на первый элемент?)
if
{

(!poPoints)
//Еслл poPoints пока еще не ссылается ни на какой
//элемент, пускай он ссылается именно на него
poPoints = poNew;

}
Оператор if проверяет, не является ли указатель p o P o i n t s нулевым. Если он нулевой, выражение ! p o P o i n t s истинно и инструкция p o P o i n t s = poNew выполняется. Если же указатель p o P o i n t s уже ссылается на какой-то элемент, новый элемент добавляется в конец списка:
poLast->poNext = poNew;
Смысл выполняемых этим кодом действий следующий: определяется элемент, на который
ссылается указатель poLast, и составляющей poNext этого элемента присваивается адрес
создаваемого элемента, на который в данный момент ссылается указатель poNew.
И наконец, обновляется значение указателя poLast так, чтобы теперь он ссылался на
только что созданный объект, и вызывается функция NewPoint, которая принимает значения координат новой точки:
poLast •= poNew;
NewPoint(poNew) ;
После того как пользователь укажет координаты всех точек, программа отобразит их на
экране и соединит между собой прямыми линиями. Этот процесс начнется с отображения
первой точки, после чего будет проведена линия к следующей точке, затем к следующей и
т.д. Процесс рисования завершится, как только программа дойдет до нулевого указателя, поскольку нулевой указатель принадлежит последнему элементу списка.
PoLast = p o P o i n t s ;
w h i l e (ooLast)
{

//Отображение линий
DrawPoint(poGraphics, p o L a s t ) ;
//Переход к следующему элементу
poLast = poLast->poNext;
}
После того как все линии будут нарисованы, необходимо очистить память от ненужной
более информации (о том, для чего это нужно делать, речь идет ниже в этой главе):
PoLast = p o P o i n t s ;
w h i l e (poLast)

Глава 13. Указатели

145

PointList *poNext;
poNext = poLast->poNext;
//Освобождение памяти
delete poLast;
//Переход к следующему элементу
poLast = poNext;

Код программы
Ниже приведен код всей программы, работа которой обсуждалась в предыдущем разделе.
//Draw
//Применение связанного списка для сохранения
//координат произвольного количества точек
//и их отображение с использованием GDI +
jfinclude "stdafx. h"

#using nX = Int32::Parse(Console::ReadLine(
Console::WriteLine(S"Введите координату Y")
poNew->nY = Int32::Parse(Console::ReadLine{
//Присвоение указателю нулевого значения
poNew->poNext = С;
//Отображение точек на экране
void DrawPoint(Graphics *poGraphics, PointList *poPoint)
ть ли точки для отображения?
If (poPoint->poNext)

/ 46 Часть II. Все, что вы хотели знать о C++, но о чем боялись спросить

//Создание пера для рисования
Pen *poPen = Pen(Color::Red);
//Рисование линии от текущей точки к следующей
poGraphics->DrawLine(poPen, poPoint->nX, poPoint->nY,
роPoint->poNext~>nX, poPoint->poNext->nY);

//С этой точки начинается выполнение программы
#ifdef JJNICODE
int wmain(void)
#else
int main(void)
#endif
{
//Указатель для первого элемента списка
PointList *poPoints = 0;
//Указатель для последнего созданного элемента
PointList *poLast;
//Указатель для нового элемента списка
PointList *poNew;
//Указатель для принятия ответа от пользователя
String *pszMore;
//Структуры для отображения графики
Form *poForm = new Form();
Graphics *poGraphics = poForm->CreateGraphics();
while (!fFinished)
<
//Выделение памяти для нового элемента
poNew = new PointList;
if •!poPoints)
{
//Если это первый элемент, присвоение его
//адреса указателю poPoints
poPoints = poNew;
)
else
{
//Присоединение элемента в конец списка
poLast->poNext = poNew;
}
//Обновление значения указателя poLast
poLast = poNew;
//Принятие от пользователя координат новой точки
NewPoint(poNew);
//Хочет ли пользователь ввести координаты для
//следующей точки?
Console::WriteLine("Нажмите у для определения
следующей точки");
pszMore = Console::ReadLine();
if (!pszMore->Equals(S"y"))

Глава 13. Указатели

147

fFinished = t r u e ;

//Отображение окна для рисования
poForm->Show();
//Отображение точек на экране
PoLast = poPoints;
while {poLast}
{

//Отображение линий
DrawPoint(poGraphics, poLast);
//Переход к следующему элементу
poLast = poLast->poNext;

//Освобождение памяти, которая использовалась для
//отображения графики на экране
poGraphics~>Dispose();
//Освобождение памяти, занятой элементами списка
PoLast = poPoints;
while (poLast)
{
PointList *poNext;
poNext = poLast->poNext;
//Освобождение памяти
d e l e t e poLast;
//Переход к следующему элементу
poLast = poNext;

//Ожидание, пока пользователь не закроет окно формы
Application::Run(poForm);

Использование ссылочных аргументов
Как уже отмечалось, изменять значения глобальных переменных в процессе выполнения функций - плохая практика. Намного разумнее использовать функции для возвращения значений и затем уже как-то
изменять эти значения вне функций. {В будущем это поможет вам избежать многих ошибок и без труда
понимать смысл выполняемых функциями действий.)
Если же функция должна изменить значения нескольких элементов или элементов, принадлежащих какой-то структуре, передайте ей в качестве параметра указатель, ссылающийся на нужный элемент. В
процессе своего выполнения функция может разыменовать указатель и изменить значение элемента. Это
намного лучше, чем изменять значения глобальных переменных {или переменных, область видимости которых распространяется за пределы этой функции), поскольку при передаче функциям значений указателей вы точно определяете, какие элементы будут изменены. Таким образом, не вникая в подробности ;
выполнения функции, вы или другие программисты сможете сразу же определить, значения каких элементов могут быть изменены этой функций.

148 Часть It. Все, что вы хотели знать о C++, но о чем боялись спросить

Другой возможностью является использование ссылочных аргументов. При этом функции передаются
указатели на аргументы (т.е. их адреса в памяти), но не сами аргументы.
Поскольку в распоряжение функции попадают адреса элементов, она может изменять их значения непосредственно.
Использовать ссылочные аргументы удобнее, чем передавать в качестве аргументов значения указателей, поскольку в этом случае не нужно разыменовывать указатели внутри функции. Чтобы определить,
что функция будет использовать ссылочный аргумент, наберите в списке аргументов перед названием
этого аргумента символ &:

int Factorialfint ^Number)

Теперь все изменения, выполненные функцией в отношении переменной Number, будут иметь постоянный
характер, и это будет выглядеть так, как будто значение переменной изменяется за пределами функции.

Воп/мсм Зезопасноопи
Как известно, большие полномочия сопровождаются большей ответственностью, что
справедливо и в отношении указателей. Язык C++обладает большими возможностями, и с
помощью указателей программист получает прямой доступ к памяти компьютера. Это означает, что использовать их нужно очень осторожно. В этом разделе затрагиваются вопросы, о
которых нужно помнить при работе с указателями, а также описываются возможности среды
.NET, которые помогут вам избежать ошибок.

Освобождение памяти
Если вы видите, что в выделенной ранее памяти больше нет необходимости, сразу же освобождайте ее. Таким образом, ваши программы не будут занимать больше памяти, чем им
требуется.
Для освобождения памяти используется команда d e l e t e , действие которой в точности
противоположно действию оператора new:
//Выделение памяти для элемента P o i n t L i s t
P o i n t L i s t * p n P o i n t = new P o i n t L i s t ;
//Освобождение только что выделенной памяти
delete pnPoint;
//Обнуление у к а з а т е л я
p n P o i n t = 0;
Обратите внимание, что при обнулении указателя не набирается звездочка (*), поскольку
обнуляется значение самого указателя, а не участок памяти, на который он ссылается.
Когда вы удаляете какой-то элемент, значение указателя не изменяется. Однако
тех данных, на которые ссылается указатель, в памяти больше нет. Таким образом, указатель по-прежнему ссылается на какой-то адрес, но этот адрес уже пуст.
Если вы его сейчас разыменуете, то получите совершенно бессмысленное значение. Поэтому каждый раз, удаляя какой-то элемент, обнуляйте указатель, который
на него ссылался. Благодаря этому вы будете точно знать, что все указатели вашей программы ссылаются на реальные данные.

Глава 13. Указатели

149

Общее нарушение защиты
Запутавшись с указателями, вы можете столкнуться с серьезными проблемами.
Такого рода ошибки обозначают термином GPF (General Protection Fault— общее
нарушение защиты). Иногда это означает, что придется закрывать некоторые
приложения или даже перезагружать компьютер. Windows отслеживает возникновение ошибок GPF и, как только их находит, останавливает выполнение программы. На рис. 13.4 показано, как может выглядеть диалоговое окно с сообщением о нарушении общей защиты.

Kiler Doc hat encountered a proble
We die tony lor I he n
i convene
i nce
I f you were in tHe mitts of Joroelhing. the information you weie workhg on
night be lost

Phsase l e i М к н л о П about lhi> piotrien.
We Iwve creeled an епя tepcrl that you can tend tous We writ Beat
th» report as confidential and anonymous.
To tt* »Mhat data this

Oefeug

I

РИС. 13.4. Если Windows столкнется с
GPF, она закроет вашу программу и отобразит
окно с предупреждением
Неизменяемые ссылочные аргументы
Если вы передаете функциям в качестве аргументов большие структуры данных, это может занять слишком много времени, поскольку при этом компьютер должен создать новые копии всех элементов, входящих в эти структуры. Чтобы ускорить этот процесс, передавайте структуру как ссылочный аргумент. В
этом случае передается только информация об адресе структуры, которая сама по себе занимает очень
мало места.
Но теперь функция получает возможность изменять данные, хранящиеся в структуре. Предположим, вам
этого не нужно. Чтобы решить этот вопрос, можно обозначить структуру как неизменяемый ссылочный
аргумент. При этом вы как бы сообщаете компилятору: "Я делаю это только для ускорения работы программы. Не изменяй данные, доступ к которым получает функция".
Вот пример объявления неизменяемого ссылочного аргумента:
i n t D r a w l t ( c o n s t P o i n t L i s t &MyPoint)

Ниже перечислен ряд причин, по которым работа с указателями может обернуться маленькой трагедией.
• S Запись информации на нулевой адрес. Например, если указатель f оо обнулен и
\
вы набираете код *foo = х, то это означает, что вы отправляете значение х
;
"туда, не знаю куда". Windows это не понравится. Это вообще никакой системе не
;
понравится. Результат таких действий будет плачевным.
: •/ Запись информации не на тот адрес. Предположим, например, что указатель f оо
\
ссылается на первый элемент списка, а указатель b a r — на последний. Если вы

150

Часть //. Все, что вы хотели знать о C++, но о чем боялись спросит

используете b a r в том месте, где должны были использовать foo, можете столкнуться с совершенно неожиданными последствиями.
Освобождение памяти без обнуления указателей. Например, после освобождения памяти вы преспокойно записываете по тому же адресу целую кипу новых
данных (при том, что теперь эта память отведена для совершенно другой информации). Результат будет ужасен.
Старайтесь не делать таких глупостей, иначе проблем не оберешься.

Генеральная уборка
В программе Draw было написано немало кодов для того, чтобы очистить память, которая
использовалась в процессе ее выполнения. Если хотите, можете воспользоваться возможностью
среды .NET, называемой сборкой мусора (garbage collection), которая автоматически выполнит
всю "грязную" работу. Если при объявлении объекта вы укажете, что память после него нужно
освобождать автоматически, среда .NET будет отслеживать его в процессе выполнения программы. Как только .NET увидит, что объект больше не используется, она освободит занимаемую им память. (Поскольку сборка мусора— дело не только грязное, но и отнимающее много
времени, .NET занимается этим тогда, когда для этого появляется удобный момент.)
Чтобы активизировать для объекта эту возможность, при его объявлении наберите в начале строки
дс, например:
дс

class

PointList

{

public:
int nX;
int nY;
PointList *poNext;

Универсальные указатели
На первый взгляд может показаться, что использовать указатели, которые могут ссылаться на все, что
угодно, - вещь удобная. При объявлении таких указателей вместо слова, обозначающего тип данных, набирается слово v o i d (что означает "пустой тип данных"}, но хотя эти указатели очень удобны (поскольку
вы можете использовать их для получения доступа к значениям любых типов), они также и очень опасны
(поскольку компилятор не может отследить правильность их использования).
Вот как можно создать такой указатель (но потом не говорите, что вас не предупреждали):

//Пусть pOff ссылается на что угодно
void *pOff;
. •
Указатели v o i d опасны {и одновременно удобны) именно тем, что для них не определен тип данных и
поэтому возникает реальная возможность неправильного использования памяти.
Предположим, что в рассмотренном ранее примере вы используете для создания связанного списка указатели v o i d и случайным образом вместо структур с информацией о координатах точек добавляете в
список числа, строки, записи о сотрудниках и еще неизвестно что. Компилятор никак не сможет определить, что здесь что-то не так. Но пользователи вашей программы непременно это заметят.
Подведем итог; используйте указатели v o i d только тогда, когда без этого никак не обойтись. J

Глава 13. Указатели

151

octnfooicax
Если вы занимались программированием раньше, то должны знать, что термином
строки обозначается текстовая информация, точнее, наборы следующих друг за
другом символов. В .NET есть встроенный класс S t r i n g , который предназначен
для работы со строками. Если же вы пишете неуправляемый код, вам придется
делать все самому, используя указатели типа char. Но не расстраивайтесь: есть
множество библиотечных функций, которые специально созданы для обработки
текстовой информации.
Строки хранятся в памяти компьютера как массивы расположенных друг за другом символов, заканчивающиеся нулевым значением. (Из-за этого нулевого байта в конце строки они
называются также строками с завершающим нулем.) Доступ к текстовой информации можно
получить с помощью указателей, которые ссылаются на первый символ строки. Чтобы создать строку, наберите такой код:
char *szMyString = "романтическое путешествие";
Этим кодом создается строка, содержащая текст "романтическое путешествие". Указатель
szMyString ссылается на эту строку. Чтобы отобразить эту строку на экране, наберите
c o u t
[
|; S
|
|
|
|

S Breakpoints (Точки останова). Дает указание отладчику остановить выполнение
программы тогда, когда будет достигнута определенная строка кодов. Этот инструмент удобен в том случае, если у вас большая программа, а вы хотите проследить за выполнением только какого-то отдельного фрагмента кода. Поставьте точку останова перед этим фрагментом и запустите программу. Отладчик выполнит
все инструкции, набранные до точки останова, и остановится, чтобы дать вам возможность далее выполнять программу пошагово.
Step Into (Пошаговое выполнение) и Step Over (Пошаговое выполнение с пере шаги вал нем через процедуры). Выполнение кодов программы по одной строке
за раз. Используйте эти средства для контроля за выполнением каждой отдельной
инструкции. Если вы будете внимательны, то обязательно обнаружите искомую
ошибку (если она там есть).

172 Часть II. Все, что вы хотели знать о C++, но о чем боялись спросить

Watches (Просмотр). Отображение текущих значений переменных в процессе выполнения программы. Это средство поможет вам проследить за жизненным циклом интересующих вас переменных и увидеть, как изменяются их значения после
выполнения тех или иных инструкций. Это же средство позволяет просматривать
значения выражений, благодаря чему вы можете видеть, как они изменяются в результате изменения значений переменных, входящих в эти выражения.

O/nuaqtUfc плюсfieqatanofi —
и €се в наших [гс/ках
Окно редактора кодов является также и окном отладчика. Другими словами, если вам
нужно совершить такие действия по отладке программы, как, например, определение точки
останова или просмотр значений переменных, сделать вы это можете прямо в окне редактора.
Процессы редактирования и отладки программы тесно связаны между собой. Иначе и
быть не может, поскольку при отладке программы необходимо просматривать исходные коды, чтобы видеть, выполнение каких из них приводит к тем или иным результатам. Так как
Visual C++ объединяет процессы редактирования и отладки, при отладке можно использовать
любые возможности редактора кодов (такие, как прокрутка окна, разделение окна или поиск
нужного слова). Вы можете даже при отладке сразу же вносить изменения в исходные коды.
Но не вся информация отображается непосредственно в окне редактора. Некоторые действия, связанные с отладкой программ, сопровождаются открытием специальных окон. Например, если вы захотите просмотреть значения переменных, для этого откроется отдельное
окно Watch. (Более подробно это описывается несколько ниже в главе.)

Оанановись, мгновение!
Точки останова позволяют остановить выполнение программы в нужном месте. Предположим, например, что у вас есть подпрограмма, вычисляющая факториал числа. По какой-то
причине возвращаемый ею результат всегда равен нулю. Чтобы определить, в чем заключается ошибка, можно установить точку останова в начале этой подпрограммы. Затем, когда вы
запустите программу, ее выполнение будет остановлено в момент вызова процедуры, вычисляющей факториал, и вы сможете использовать все средства отладчика для определения, что
именно в этой процедуре работает не так. Забегая вперед, скажем, что весь материал, изложенный далее в главе, посвящен решению подобных проблем.
Установить точку останова довольно просто. В окне редактора кодов щелкните правой
кнопкой мыши в том месте, где нужно установить точку останова, и в открывшемся меню
выберите команду Insert Breakpoint (Установить точку останова). Слева от выбранной строки появится значок точки останова. Ну хорошо, этим значком будет красный кружок, а не
ромбик или что-то еще, но главное, чтобы вы поняли общую идею,
Если вы установили точку останова и не видите слева от строки соответствующий
значок, значит, у вас, скорее всего, отключена возможность отображения дополнительной информации на границе строк. Выберите команду Tools ^ O p t i o n s
(Сервис^Параметры), чтобы открыть диалоговое окно Options (Параметры). В
категории Text Editor/General (Редактор/Общие) выберите General (Общие) и
затем опцию Selection Margin (Выделение на границе). Щелкните на кнопке ОК,
и знак точки останова появится напротив выбранной строки.

Глава 16. Через тернии к... работающей программе

173

Удаляется точка останова так же просто, как и устанавливается. В окне редактора кодов
щелкните правой кнопкой мыши на строке, для которой установлена точка останова, и в открывшемся меню выберите команду Remove Breakpoint (Удалить точку останова). Расположенный напротив этой строки значок точки останова исчезнет с экрана.
Точки останова не исчезают после перезагрузки системы. Другими словами, если вы
установили точку останова и не удаляли ее, а позже вновь открыли этот же проект,
созданная точка останова по-прежнему будет на своем месте. Это очень удобно, поскольку процесс отладки программы может затянуться на какое-то время и вы можете прерывать его на то, чтобы, например, поспать, перекусить или побродить по
Internet. Затем, когда вы снова вернетесь к отладке программы, вам не придется заново восстанавливать всю картину и расставлять прежние точки останова. Однако с
другой стороны, если программа уже будет работать так, как нужно, но вы забудете
убрать какие-то точки останова, то она потом непременно выдаст вам парочку
"сюрпризов". (Выполнение программы будет прерываться каждый раз, когда в кодах будут встречаться точки останова.) Если вы не уверены, все ли точки останова
удалены, выберите команду Debug^Windows^Breakpoints (Отладка^Окна 1 ^
Точки останова). В результате откроется окно, в котором будут показаны все существующие на данный момент точки останова.

Шаг за шагом: движение к и,ели
После того как точка останова достигнута, самое время проследить, как будет выполняться каждая последующая инструкция. Сделать это можно, выполняя по одной строке кода за
раз. Такие действия называются пошаговым выполнением программы.
Есть два варианта: использовать команду Step Into (Пошаговое выполнение) либо команду Step Over (Пошаговое выполнение с перешагиванием через процедуры). Если в процессе
использования команды Step Into программа вызывает какую-то функцию, все ее коды также
выполняются пошагово. Если программа вызывает функцию в процессе использования команды Step Over, все коды функции выполняются сразу же, без остановки на каждом шаге.
Команду Step Into следует использовать в том случае, если какая-то часть программы работает не так, как нужно, но вы не уверены, содержится ли ошибка в этом фрагменте кода
или в той функции, которая при этом вызывается. Команда Step Over используется в том
случае, если проверяемые коды должны выполняться пошагово, а вызываемые при этом
функции — как единое целое.
Рассмотрим это на таком примере:

foo = MyRoot(x);
foo = foo + 1;
Если вы точно знаете, что функция MyRoot работает правильно, и хотите только проверить, как будет выполнена строка f o o + l , можете использовать команду Step Over. Первая
строка будет выполнена за один шаг (включая все инструкции, из которых состоит функция
MyRoot), и затем на втором шаге будет выполнена строка f o o = f o o + l .
Если вы сомневаетесь в правильности выполнения функции MyRoot, используйте команду Step into. При этом, выполняя первую строку, вы перейдете к первой строке функции
MyRoot. Затем пошагово, одна за другой, будут выполняться все строки этой функции, и если вам повезет, вы найдете там свою ошибку.
В процессе пошагового выполнения программы слева, напротив строки, которая должна
выполниться на следующем шаге, будет отображаться желтая стрелка. Если для этой строки
определена точка останова, стрелка будет отображаться сверху над красным кружком.

174 Часть //. Все, что вы хотели знать о C++, но о чем боялись спросить

Если при пошаговом выполнении программы вы не видите желтой стрелки, значит, у вас, скорее всего, отключена возможность отображения дополнительной
1
информации на границе строк. Выберите команду Tools ^ Options, чтобы открыть диалоговое окно Options. В категории Text Editor/General выберите General и затем опцию Selection Margin. Щелкните на кнопке ОК, и желтая стрелка
появится на экране.
Для пошагового выполнения с перешагиванием через процедуры нажимайте клавишу
или щелкайте на кнопке Step Over панели Debug (Отладка), показанной на рис. 16.1.
Если вызываемые функции также должны выполняться пошагово, нажимайте клавишу
или щелкайте на кнопке Step Into.

Рис. 16.1. На панели Debug расположены
кнопки, управляющие процессом выполнения программы

Обратите внимание, что команды Step Into и Step Over могут быть использованы без
предварительного определения точек останова. Если вы сделаете это, пошаговое выполнение
будет начато с самой первой строки программы.

JToC4iO/ft/tUM, t/ИО ПОЛЕЧИЛОСЬ
Занимаясь отладкой программы, очень важно знать, правильный ли результат возвращает
формула, какое значение имеет переменная или что сохранено в качестве элемента массива.
Visual C++ предлагает несколько способов просмотра значений переменных и получаемых
при обработке выражений результатов.
Окно Autos (Автоматическое) открывается автоматически, когда вы приступаете к отладке программы (рис. 16.2). В нем отображаются названия и значения переменных, которые используются в текущем и предыдущем выражениях. Лично вам не нужно указывать имена переменных — они сами и их значения отображаются автоматически, по мере того как вы переходите от одной строки программы к другой.

РИС. 16.2. В окне Autos отображаются имена и значения переменных, используемых в предыдущем и текущем выражениях

Также при отладке программы автоматически отображается окно Locals (Локальные), показанное на рис. 16.3. Как и в окне Autos, в нем автоматически отображаются названия переменных и их значения. Но здесь уже отображаются все локальные переменные той функции,
коды которой выполняются в данный момент (т.е. переменные, объявленные внутри этой
функции).

Глава 16. Через тернии к... работающей программе

175

Рис. 16.3. В окне Locals отображаются имена и значения всех локальных переменных
Окна Autos и Locals предоставляют полезную информацию, но, поскольку они сами определяют, значения каких переменных должны отображаться, иногда они не очень удобны.
Но в этом нет ничего страшного, так как в Visual C++ есть другие средства для контроля за
значениями переменных. В окне Watch (Просмотр) вы можете сами указать, какие именно
значения вас интересуют. Всего есть четыре окна Watch, пронумерованных от Watchi до
Watch4, что позволяет выделить несколько наборов переменных и выражений, значения которых вы хотите проконтролировать.
Чтобы просмотреть значение какого-либо объекта, используя окно Watch, выполните
следующее.
1.

В окне Watch щелкните на пустой строке в колонке Name (Имя).

2.

Наберите имя объекта, значение которого вы хотите видеть.

3.

Нажмите клавишу .

Каждый раз, когда значение объекта изменяется, информация в окне Watch обновляется. Например, на рис. 16.4 показано окно Watch со значениями трех числовых переменных. Как только значение переменной nNumber, n R e s u l t или i изменится, окно
Watch будет обновлено.

Watch 1
Name
nNumber
nResult
' . i"

Value
5
0
' •' 2

j Type
int32
int32
::
'. . ' ' -* '_W32

QAute'CKc: Ь ££3 Watch 1
Рис. 16.4. Окно Watch позволяет видеть, как изменяются значения интересующих
вас переменных в процессе выполнения программы

Не нравится значение — измените его
Если это необходимо, вы можете даже сами изменять значения просматриваемых переменных. Для этого выполните следующее.
1.

Выделите переменную, значение которой вы хотите изменить.

2.

Выделите значение этой переменной.

3.

Наберите новое значение.

4.

Нажмите клавишу .

176

Часть II. Все, что вы хотели знать о C++, но о чем боялись спроси

Эта возможность может вам пригодиться для того, чтобы проверить, правильно ли будет
работать вся остальная программа, если переменная будет иметь корректное значение. Когда
вы это проверите, вам нужно будет вернуться назад, чтобы определить, почему эта переменная принимает неверное значение.

Торопитесь? Нет проблем!
Visual C++ позволяет просматривать значения переменных и выражений без необходимости добавления их в окно Watch, используя для этого средство QuickWatch.
Чтобы использовать QuickWatCh для просмотра значения какого-либо объекта, выполните в
окне редактора следующие действия: щелкните правой кнопкой мыши на названии интересующего вас элемента и выберите в открывшемся меню команду QuickWatch. Откроется диалоговое окно QuickWatch (рис. 16.5). Затем, если вы захотите перенести выбранный объект из окна
QuickWatch в окно Watch, просто щелкните на кнопке Add Watch (Добавить в окно Watch).

I Пяти
nNunbw

Value
5

Рис. 16.5. Значения переменных можно просматривать, не добавляя их в окно Watch

Очень торопитесь?
Средства Watch и QuickWatch удобны, но все же, чтобы их использовать, вам придется
несколько раз щелкнуть кнопкой мыши. Представим, что вы этого делать не хотите. Тогда
Visual C++ может предложить другое средство, которое называется DataTips (Подсказка).
Оно позволяет увидеть значение переменной без единого щелчка кнопкой мыши.
Чтобы просмотреть значение переменной, используя средство DataTips, выполните следующее.
1.
2.

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

Рядом с курсором появится подсказка, в которой будет показано текущее значение этой
переменной (рис. 16.6).

Глава 16. Через тернии к... работающей программе

177

i I X

5 ?.i - Риде BadFact.cpp
j(Globals)

••j

•wmain

rtefse

J
—-

intmain(void)
#endif
intnNumber;
///types
/Getnu0mbersfromtheuser,untiltheuser
i umber=GetNumberQ)
о while(fN
i -f //N
n
(N
mbew
re
=wi
5|l outputtheresult
ouw
//Notethatwearecallingthefunction
//Factorial
Console:WrtieLn
i e(S"Thefactorialof{0}is{1}",nNu
//Nowwearefinished
//Hangoutuntiltheusethe
risenter
finished
Console::Wntel_ine(L"Hit
key to stop the ргодгг
- — •

Console::ReadLine();

return0;

-



• Г

Рис. 16.6. DataTips показывает значения переменных без каких-либо усилий
с вашей стороны

Тене/ьальналi/