В предыдущем разделе мы создали при помощи средств MFC AppWizard работающий шаблон приложения. Теперь мы усовершенствуем его, чтобы пользователь смог рисовать в окне приложения и сохранять свои рисунки в файле на диске.
За основу нашего нового приложения мы возьмем проект Single и внесем в него все необходимые исправления и добавления. Доработаем приложение Single так, что когда пользователь нажимает левую клавишу мыши в окне отображается окружность, а когда пользователь нажимает правую кнопку - то отображается квадрат.
В момент нажатия на клавиши мыши создаются соответствующие сообщения, которые передаются классу окна просмотра. Нажатие левой клавиши мыши вызывает сообщение WM_LBUTTONDOWN, а нажатие правой - сообщение WM_RBUTTONDOWN.
Чтобы класс окна просмотра CSingleView мог отреагировать на это сообщение, вы должны создать метод для его обработки. Лучше всего для этого воспользоваться средствами ClassWizard.
Откройте страницу Message Maps на панели ClassWizard. Выберите из списков Class name и Object IDs класс CSingleView. В списке Messages появится названия виртуальных методов, которые вы можете переопределить и сообщений, для которых можно создать методы обработки.
Выберите из списка Messages сообщение WM_LBUTTONDOWN и нажмите кнопку Add Function. ClassWizard добавляет новую строку в таблицу сообщений класса CSingleView, вставляет в класс описание нового метода обработчика сообщения и создает шаблон этого метода.
Нажмите кнопку Edit Code. В окне редактирования появится шаблон метода, предназначенного для обработки сообщения WM_LBUTTONDOWN.
void CSingleView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Здесь вы можете разместить код метода
CView::OnLButtonDown(nFlags, point);
}
Название этого метода ClassWizard выбирает автоматически на основе сообщения WM_LBUTTONDOWN. Для этого префикс WM_ в названии сообщения заменяется префиксом On и происходит замена некоторых прописных букв строчными.
Шаблон метода OnLButtonDown содержит вызов метода OnLButtonDown базового класса CView. Вы должны добавить свой код перед вызовом этого метода, сразу после коментария // TODO:.


Когда вы создаете приложение с однооконным или многооконным интерфейсом при помощи средств MFC AppWizard, в последней диалоговой панели вы можете выбрать базовый класс для окна просмотра приложения. По умолчанию используется класс CView.
От класса CView наследуется целая группа классов, которые можно использовать для управление окном просмотра документа. Каждый из этих классов предоставляет дополнительные возможности для отображения документа.
CEditView <- |<- CCtrlView <- | CView <- CWnd
CRichEditView <- | |
CListView <- | |
CTreeView <- | |
|
CFormView <- CScrollView <- |
Опишем основные характеристики этих классов.
| Класс | Описание | ||
| CView | Наиболее общий класс, обеспечивающий отображение документа и взаимодействие с пользователем | ||
| CScrollView | Класс CScrollView наследован от базового класса CView. В этом классе добавлены полосы просмотра, позволяющие перемещать документ в окне | ||
| CEditView | Класс наследован от класса CView. Класс CEditView предоставляет возможности простого текстового редактора | ||
| CRichEditView | Класс наследован от класса CView. Класс предоставляет возможности текстового редактора. В отличие от CEditView, он позволяет работать с текстом в формате RTF | ||
| CFormView | Класс обеспечивает форматированное отображение документа на основе диалоговой панели | ||
| CListView | Класс обеспечивает отображение документа с использование спискового органа управления | ||
| CTreeView | Класс обеспечивает отображение документа с использование древовидного органа управления |
Чтобы создать простейший текстовый редактор создайте новое приложение с однооконным (или многооконным) интерфейсом. В качестве базового класса для класса окна просмотра приложения выберите класс CEditView. Завершите создание шаблона приложения.
Постройте полученный проект и запустите приложение. Текстовый редактор готов! Вы можете открыть в нем любой текстовый файл, отредактировать его и сохранить, внесенные изменения.
|
Строка меню Edit |
Описание |
|
Undo |
Отменить последнюю операцию |
|
Cut |
Удалить выделенный текст и записать его в clipboard |
|
Copy |
Скопировать выделенный текст в clipboard |
|
Paste |
Вставить в документ содержимое clipboard |
MFC содержит классы, соответствующие объектам типа простых геометрических фигур, текстовых строк и объектам, определяющим дату и время. В следующей таблице перечислены названия этих классов и их краткие описания.
| Класс | Описание | ||
| CPoint | Объекты класса описывают точку | ||
| CRect | Объекты класса описывают прямоугольник | ||
| CSize | Объекты класса определяют размер прямоугольника | ||
| CSrting | Объекты класса представляют собой текстовые строки переменной длинны | ||
| CTime | Объекты класса служат для хранения даты и времени. Большое количество методов класса позволяют выполнять над объектами класса различные преобразования | ||
| CTimeSpan | Объекты класса определяют период времени |
Метод AssertValid выполняет проверку целостности объекта класса. Он проверяет состояние элементов данных класса на недопустимые значения. Если вы работаете с отладочной версией приложения и метод AssertValid обнаружит ошибку во внутреннем представлении объекта класса, выполнение приложения прерывается с выдачей соответствующего сообщения.
virtual void AssertValid() const;
Если вы наследуете класс от базового класса CObject и желаете использовать возможности метода AssertValid, вы должны переопределить его. Переопределенный метод AssertValid должен вызывать метод AssertValid базового класса, чтобы проверить целостность соответствующей части объекта. Затем необходимо выполнить проверку элементов порожденного класса. Для этого используйте макрокоманду ASSERT:
ASSERT(booleanExpression)
Макрокоманда ASSERT проверяет свой параметр booleanExpression. Если параметр макрокоманды имеет нулевое значение (FALSE), она отображает диагностическое сообщение и прерывает работу приложения. Если параметр booleanExpression не равен нулю (TRUE) работа приложения продолжается и макрокоманда не выполняет никаких действий.
Макрокоманда ASSERT выполняет проверку только в отладочной версии приложения. В окончательной версии приложения, построенной без отладочной информации, макрокоманда ASSERT не работает.
Если проверку параметра макрокоманды необходимо выполнять и в окончательной версии приложения, вы можете использовать вместо макрокоманды ASSERT макрокоманду VERIFY. Но при обнаружении ошибки работа приложения не будет прервана.
Вот пример переопределения метода AssertValid для класса CFigure, наследованного от базового класса CObject:
// Класс CFigure наследуется от базового класса CObject
class CFigure : public CObject
{
// Переопределяем виртуальный метод базового класса
int m_area = 0;
// Остальные элементы класса...
}
// Переопределяем виртуальный метод AssertValid класса CObject
void CFigure::AssertValid() const
{
// Сначала проверяем целостность элементов базового класса
CObject::AssertValid();
// Проверяем элемент m_area.
// Он должен быть больше или равен нулю
ASSERT(m_area >= 0);
}
Стандартная библиотека компиляторов содержит специальные функции управления памятью - malloc, free, а также другие разновидности этих функций. Они позволяют получить для использования блок оперативной памяти, и затем отдать его обратно операционной системе.
В Си++ встроены специальные операторы для управления памятью - оператор new и оператор delete. Эти операторы очень удобны для динамического создания переменных, массивов и объектов классов, поэтому мы остановимся на них более подробно.
Мы уже рассказывали, что можно управлять доступом к элементам класса, указывая спецификаторы доступа для элементов класса. Элементы класса, объявленные с спецификаторами protected и private доступны только из методов самого класса. Элементы с спецификаторами public доступны не только из методов класса, но и извне.
При создании порожденного класса встает вопрос о доступе к элементам базового класса. Оказывается, когда вы наследуете класс из базового класса, вы можете управлять разграничением доступа к элементам базового класса. При этом имеет значение то, как объявлены элементы базового класса и какой спецификатор доступа указан для базового класса.
По следующей таблице вы можете определить как будут доступны элементы базового класса в зависимости от спецификатора доступа базового класса и спецификаторов доступа элементов базового класса.
| Спецификатор доступа базового класса | |||||||
| Спецификатор доступа элемента базового класса | public | protected | private | ||||
| public | Доступны как public | Доступны как protected | Доступны как private | ||||
| protected | Доступны как protected | Доступны как protected | Доступны как private | ||||
| private | Недоступны | Недоступны | Недоступны |
Определив класс, вы можете создавать объекты этого класса и манипулировать ими, используя методы. Некоторые данные и методы, объединенные одним классом, можно сделать недоступными вне реализации класса, к другим можно будет обращаться из программы.
Для управления доступом к элементам класса предусмотрены ключевые слова public, private и protect (спецификаторы доступа). Методы и данные, определенные или описанные после ключевого слова public представляют собой интерфейс класса - они доступны для использования вне определения класса. Остальные члены класса относятся к его внутренней реализации и обычно недоступны вне класса. Различия между членами класса, описанными после ключевых слов private и protect сказываются только при наследовании от данного класса новых классов. Процедуру наследования мы рассмотрим позже.
Ключевые слова public, private и protect указываются в определении класса перед элементами класса, доступом к которым они управляют. Ключевые слова, управляющие доступом, могут быть указаны несколько раз в одном классе, порядок их расположения значения не имеет. По умолчанию элементы класса являются private. Рекомендуется всегда явно определять права доступа к членам класса.
Ниже представлено определение класса Sample:
class Sample
{
int iX;
void Load();
public:
void SetStr();
void GetStr();
char sDataText[80];
private:
char sNameText[80];
int iIndex;
public:
void ConvertStr();
int iLevel;
};
В классе описаны элементы данных iX, sDataText, sNameText, iIndex, iLevel и методы Load, SetStr, GetStr, ConvertStr.
Элементы данных и методы SetStr, GetStr, sDataText, ConvertStr, iLevel объявлены public. К ним можно обращаться как из методов класса Sample, так и из программы. Остальные элементы класса объявлены как private. Доступ к ним открыт только для методов самого класса, а также дружественных функций и дружественных методов других классов. Дружественные функции и дружественные классы описаны в следующем разделе.
Временное меню данных класса отличается от временного меню метода. Если выбрать из списка элементов класса данные и нажать правую кнопку мыши, на экране появится меню, представленное на рисунке 2.19.

Рис. 2.19. Временное меню метода
Выберите из списка элементов класса интересующий вас метод и нажмите правую кнопку мыши. На экране появится временное меню, показанное нами на рисунке 2.18. Это меню позволяет перейти к редактированию объявления или определения метода, просмотреть те строки исходного текста приложения, в которых вызывается метод, получить список функций и методов, вызываемых выбранным методом.

Рис. 2.18. Временное меню метода
Чтобы открыть в редакторе файл, в котором объявляется метод и перейти к его редактированию, выберите из временного меню метода строку Go to Definition. После того как вы добавите в класс новый метод, ClassWizard создаст шаблон метода. Строка Go to Declaration из временного меню метода позволяет изменить этот шаблон по вашему усмотрению.
Возможности ClassView можно использовать даже на этапе отладки приложения. Так, из временного меню метода можно установить точку прерывания непосредственно на начало метода. Для этого выберите из меню строку Set Breakpoint.
Добавить в проект новый ресурс очень просто. Вы можете для этого воспользоваться кнопками панели управления Progect или установить указатель мыши на название типа ресурса и нажать правую кнопку мыши. На экране появится временное меню свойств данного ресурса.
Если у вас уже есть ресурсы, которые разработаны ранее и записаны в отдельных файлах на диске, вы можете подключить их к своему проекту. Для этого надо выбрать из временного меню свойств ресурса строку Import. На экране появится диалоговая панель Import Resource с приглашением ввести имя файла подключаемого вами ресурса. Новый ресурс проекта будет записан в подкаталог RES, расположенный в каталоге проекта.
Сам по себе новый ресурс, подключенный к приложению, не принесет никакой пользы. Ваше приложение должно обращаться к нему - загружать в память, обрабатывать сообщения, связанные с этим ресурсом
Среда Visual C++ версии 4.0 не только позволяет просматривать и редактировать ресурсы приложения, она позволяет сразу после создания ресурса вызвать ClassWizard и подключить к ресурсу управляющий программный код. Пока мы не будем использовать ClassWizard, а код, управляющий ресурсами приложения, создадим вручную.
Большую часть пользовательского интерфейса любого приложения составляют ресурсы - меню, диалоговые панели, пиктограммы, курсоры. Создавая приложение, MFC AppWizard подготавливает для него базовый набор ресурсов. Вы можете редактировать подготовленные для вас ресурсы по своему усмотрению, а также добавлять в проект новые ресурсы.
Все ресурсы приложения хранятся в отдельном каталоге. Этот каталог называется RES и располагается в главном каталоге проекта. Однако нет необходимости каждый раз вручную открывать файлы с ресурсами. Для этого надо использовать средства, предоставляемые средой Visual C++.
Просмотреть ресурсы приложения можно в окне Project Workspace на странице ResourceView. Ресурсы приложения представлены в виде дерева и разделяются по типам. Чтобы просмотреть ресурсы определенного типа, надо установить курсор на символ



| Ресурс | Описание | ||||
![]() | Accelerators | Акселераторы | |||
![]() | Bitmaps | Растровые изображения в формате BMP | |||
![]() | Cursors | Курсоры | |||
![]() | Dialogs | Диалоговые панели | |||
![]() | Icons | Пиктограммы | |||
![]() | Menus | Меню | |||
![]() | String tables | Таблицы текстовых строк | |||
![]() | Toolbars | Панели управления | |||
![]() | Version information | Сведения о версии приложения | |||
![]() | Имя определяется программистом | Ресурс, определяемый самим пользователем |
MFC AppWizard автоматически создает несколько различных ресурсов для приложения Dialog. Вы можете просмотреть эти ресурсы в окне Project Workspace, выбрав страницу ResourceView. Как видите ресурсы приложения включают две диалоговые панели, пиктограмму, таблицу строк и информацию о версии приложения. Рассмотрим эти ресурсы подробнее.
Приложение с однооконным интерфейсом, созданное средствами MFC AppWizard, имеет гораздо больше ресурсов, чем приложение, использующее в качестве интерфейса обыкновенную диалоговую панель. В нем определены не только диалоговые панели, таблица текстовых строк, пиктограмма и ресурс описания версии приложения, но также меню, панель управления и таблица акселераторов.
Подавляющее большинство классов библиотеки MFC наследовано от базового класса CObject, лежащего в основе всей иерархии классов этой библиотеки. Методы и элементы данных класса CObject представляют наиболее общие свойства наследованных из него классов MFC.
Класс CObject, а также все классы, наследованные от него, обеспечивают возможность сохранения объектов класса в файлах на диске с их последующим восстановлением.
Для объектов классов, наследованных от базового класса CObject уже во время работы приложения можно получить разнообразную информацию о классе объекта.
Ряд методов класса CObject предназначен для получения дампа объектов класса во время отладки приложения. Эта особенность класса может ускорить процесс поиска ошибок в приложении.
Еще один важный класс, наследуемый от CCmdTarget, называется CDocTemplate. От этого класса наследуются два класса CSingleDocTemplate и CMultiDocTemplate. Все эти классы предназначены для синхронизации и управления основными объектами представляющими приложение - окнами, документами и используемыми ими ресурсами.
Большой интерес для нас представляет ресурс, описывающий меню приложения. В ресурсах приложения определен только один шаблон меню, имеющий идентификатор IDR_MAINFRAME.
Когда пользователь выбирает строки меню, операционная система передает командное сообщение главному окну приложения.
//////////////////////////////////////////////////////////////
// Меню
IDR_MAINFRAME MENU PRELOAD DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&New\tCtrl+N", ID_FILE_NEW
MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN
MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE
MENUITEM "Save &As...", ID_FILE_SAVE_AS
MENUITEM SEPARATOR
MENUITEM "&Print...\tCtrl+P",ID_FILE_PRINT
MENUITEM "Print Pre&view", ID_FILE_PRINT_PREVIEW
MENUITEM "P&rint Setup...", ID_FILE_PRINT_SETUP
MENUITEM SEPARATOR
MENUITEM "Recent File", ID_FILE_MRU_FILE1,GRAYED
MENUITEM SEPARATOR
MENUITEM "E&xit", ID_APP_EXIT
END
POPUP "&Edit"
BEGIN
MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO
MENUITEM SEPARATOR
MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT
MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY
MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE
END
POPUP "&View"
BEGIN
MENUITEM "&Toolbar", ID_VIEW_TOOLBAR
MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR
END
POPUP "&Help"
BEGIN
MENUITEM "&About Single...", ID_APP_ABOUT
END
END
Большая часть строк меню IDR_MAINFRAME имеет стандартные идентификаторы, описанные в библиотеке MFC. Некоторые из команд, соответствующих этим идентификаторам полностью обрабатываются MFC. Список стандартных команд с их описанием представлен в разделе “Стандартные команды”.
Языки программирования С и Си++ обеспечивают строгую проверку типов данных. Некоторые языки не обеспечивают такой проверки и она полностью ложится на плечи программиста. Например в языке PL1 вы можете сравнивать значение строковой и числовой переменных. Это не будет ошибкой с точки зрения компилятора. Если вы случайно допустите ошибку, то обнаружить ее будет достаточно сложно.
Однако строгая типизация, присущая некоторым языкам, не всегда удобна. Если вам надо выполнять одинаковые вычисления над переменными различных типов, придется создавать две фактически одинаковые функции, которые оперируют с различными типами данных.
Аналогичная ситуация возникает, если вам надо создать класс, для работы с различными типами данных. Допустим вам надо создать список из элементов. Удобнее всего это сделать при помощи классов. Но если вам надо, чтобы в качестве элементов списка фигурировали различные типы данных, то вам наверняка придется написать несколько одинаковых классов, различающихся только типом элементов.
Естественно такая ситуация усложняет работу программиста, увеличивают объем исходных текстов программы, и наконец просто может стать причиной ошибок и несоответствий. Так, если в вашей программе содержится несколько функций или классов, отличающихся только типом данных, которыми они оперируют, то когда вы вносите исправления в одну функцию, надо будет точно также исправлять и все остальные.
Чтобы облегчить программисту работу, в стандарт языка Си++ было включено понятие шаблона. В Visual C++ шаблоны реализованы начиная с версии 2.0. Ранние версии Visual C++ с ними работать не могли.
Шаблоны предназначены для создания ряда классов и функций, отличающихся только типом обрабатываемых ими данных. Для определения шаблона предназначено ключевое слово template (шаблон). Общий синтаксис определения шаблона имеет следующий вид:
template <template-argument-list> declaration;
Аргумент template-argument-list представляет собой список условных имен для определения типов, по которым будут различаться различные реализации классов или функций данного шаблона.
В библиотеке классов MFC определен ряд шаблонов для создания таких структур хранения информации как массив, список, словарь. Более подробно об этих шаблонах вы можете прочитать в разделе “Коллекции” главы “Некоторые классы MFC”.
Библиотека MFC позволяет создавать многозадачные приложения. Для синхронизации отдельных задач приложения предусмотрен ряд специальных классов. Все они наследуются от класса CSyncObject, представляющего собой абстрактный класс (рис. 2.8).

Рис. 2.8. Класс CSyncObject
В некоторых случаях требуется, чтобы участок программного кода мог выполняться только одной задачей. Такой участок называют критической секцией кода. Для создания и управления критическими секциями предназначены объекты класса CCriticalSection.
Объекты класса CEvent представляют событие. При помощи событий одна задача приложения может передать сообщение другой.
Объекты класса CMutex позволяют в данный момент времени представить ресурс в пользование только одной задачи. Остальным задачам доступ к ресурсу запрещается.
Объекты класса CSemaphore представляют собой семафоры. Семафоры позволяют ограничить количество задач, которые имеют доступ к какому-либо ресурсу.
Весной 1996 года вышла новая версия Microsoft Visual C++ 4.1. В состав этой версии продукта включены дополнительные средства для разработки приложений, поддерживающих глобальную сеть Internet, язык моделирования виртуальной реальности (Virtual Reality Modeling Language - VRML), игровое SDK (Windows 95 Games SDK) и большое количество OCX объектов, которые вы можете включать в свои приложения.
Поддержка сети Internet
Последние версии Microsoft Internet Information Server, используемого в качестве серверов WWW и FTP, имеют программный интерфейс (Internet Server application programming interface - ISAPI). Используя этот интерфейс, вы можете разрабатывать собственные модули, взаимодействующие с сервером.
Чтобы облегчить программистам создание таких модулей, Microsoft Visual C++ версии 4.1 содержит “волшебник” ISAPI Extention Wizard, работающий на тех же принципах, что и MFC AppWizard. ISAPI Extention Wizard использует новые классы библиотеки MFC - CHttpServer, CHttpFilter, CHttpServerContext, CHttpFilterContext и CHtmlStream.
Язык моделирования виртуальной реальности
Язык моделирования виртуальной реальности позволяет помещать на WEB страницах серверов WWW трехмерные изображения. Подключившись к такой странице, пользователь сможет не просто просматривать статические изображения, он сможет перемещаться по трехмерному виртуальному пространству, выбирать различные объекты и т. д.
Игровое SDK
В настоящее время существует очень мало компьютерных игр, созданных специально для работы в операционной системе Windows. Обычно эти игры достаточно примитивны и не далеко ушли от таких игр как пасьянс и минер, поставляемых вместе с Windows. В то же время игры, которые не нуждаются в средствах Windows, значительно сложнее и имеют несравненно более сложный и реалистичный интерфейс.
Отсутствие сложных игр для Windows связано в первую очередь с тем, что в этой операционной системе нет средств для быстрого отображения графической информации на экране. Существующие методы не позволяют приложениям Windows отображать информацию с достаточной для компьютерных игр скоростью.
С появлением операционной системы Windows 95 ситуация начала изменяться к лучшему. В Windows 95 встроен специальный набор функций, обеспечивающий приложениям высокоэффективный доступ к видеоадаптеру. Вышли первые игры для Windows 95. Так фирма idSoftware выпустила специальную версию популярной игры Doom 2, предназначенную для работы исключительно в среде Windows 95.
Для создания программ, поддерживающих этот набор функций, необходимо использовать специальной средство разработки Games SDK. Ранее оно поставлялось отдельно, а начиная с версии 4.1 компилятора Microsoft Visual C++, вошло в состав этого пакета.
Набор OCX объектов
По сравнению с Microsoft Visual C++ версии 4.0 в версии 4.1 значительно расширен набор органов управления OLE (OCX). В него Microsoft включила органы управления OCX, разработанные другими фирмами.
Словарь, это таблица переменной длины, состоящая из двух колонок. Первая колонка содержит ключевые поля, а вторая - соответствующие им значения. Пользуясь объектами этого класса, вы можете по ключевому полю получить связанное с ним значение. Для поиска по ключу используется алгоритм хеширования, реализация которого вручную заняла бы слишком много времени.
В MFC включены классы для создания словарей отображающих значения слов, указателей, указателей на объекты типа CObject и объектов CString. Библиотека MFC версии 4.0 содержит шаблоны для организации словарей. Шаблоны позволяют создать класс словарей, основанных на объектах любых типов.
Ниже представлен прототип шаблона CMap.
template <class KEY, class ARG_KEY, class VALUE,
class ARG_VALUE> class CMap : public CObject
Параметр шаблона KEY определяет тип ключевого поля словаря. Параметр ARG_KEY является типом данных, используемых в качестве аргумента KEY. Параметр VALUE соответствует типу элементов, хранящихся в словаре, а параметр ARG_VALUE используется в качестве аргумента VALUE.
Построенное вами приложение можно использовать для рисования и печати документов, но оно не позволяет сохранять и загружать документ из файла на диске. Вы можете выбрать строку Save As (сохранить под именем) из меню File. На экране появится диалоговая панель Save As. В этой панели вы можете ввести имя файла, в котором надо сохранить документ. Однако несмотря на то, что файл создается, документ в него не записывается - файл остается пустым.
Вы можете попытаться его открыть, выбрав из меню File строку Open. Однако единственным результатом будет изменение заголовка окна. Чтобы приложение обрело возможность сохранения документов в файле и последующего чтения, надо изменить метод Serialize класса документа CSingleDoc.
Метод Serialize вызывается всякий раз когда надо сохранить документ в файле на диске или загрузить его из существующего файла. В частности, метод Serialize вызывается, когда пользователь выбирает из меню File строки Save, Save As и Open. Основные принципы работы метода Serialize были рассмотрены нами в разделе “Запись и восстановление объектов”.
MFC AppWizard подготавливает шаблон метода Serialize для класса CSingleDoc, представляющего документ приложения.
//////////////////////////////////////////////////////////////
// Метод Serialize класса CSingleDoc отвечает за сохранение и
// последующее восстановление документов приложения
void CSingleDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: Здесь выполняется сохранение документа
}
else
{
// TODO: Здесь выполняется загрузка документа
}
}
Вы должны определить в методе Serialize, как он должен сохранять и восстанавливать документы приложения. Так как документ, с которым работает наше приложение представлен классом CSingleDoc, то все что должен делать метод Serialize - это сохранять все элементы массива arrayFig.
//////////////////////////////////////////////////////////////
// Метод Serialize класса CSingleDoc
В классе CObject определены метод IsSerializable и виртуальный метод Serialize, которые используются для сохранения и восстановления состояния объектов в файлах на диске. Чтобы объекты класса можно было сохранять в файлах на диске с возможностью их последующего восстановления, объявление класса объекта должно содержать макрокоманду DECLARE_SERIAL, а реализация класса макрокоманду IMPLEMENT_SERIAL. Более подробно об сохранении и восстановлении объектов можно прочитать в разделе “Запись и восстановление объектов”.
Для тех, кто занимается сетевыми коммуникациями, в состав библиотеки MFC включены классы CAsyncSocket и наследованный от него класс CSocket (рис. 2.9). В класс CAsyncSocket включен программный интерфейс Windows Socket.
Класс CSocket предоставляет программисту более высокий уровень для работы с сокетами. Это значительно облегчает задачу программирования сетевых приложений.
Программирование на уровне программного интерфейса Windows с использованием сокетов описано в двадцать третьем томе серии “Библиотека системного программиста”, который называется “Глобальные сети компьютеров”.

Рис. 2.9. Класс CAsyncSocket
Эта группа включает в себя сообщения WM_COMMAND от дочерних окон (включая окна стандартных классов), передаваемые их родительскому окну. Сообщения от органов управления обрабатываются точно таким же образом, что и оконные сообщения.
Исключение составляет сообщение WM_COMMAND с кодом извещения BN_CLICKED. Это сообщение передается кнопкой, когда пользователь на нее нажимает. Обработка сообщений с кодом извещения BN_CLICKED от органов управления происходит аналогично обработке командных сообщений.
Так как наше приложение будет содержать меню, мы приступим к созданию ресурсов приложения. Для этого создайте новый файл ресурсов. Выберите из меню File строку New, а затем из открывшейся диалоговой панели выберите строку Resource Script и нажмите кнопку OK.
Теперь надо создать меню. Выберите из меню Insert строку Resource. На экране появится диалоговая панель Insert Resource. Выберите из нее строку Menu и нажмите кнопку OK. Вы сможете в диалоговом режиме разработать меню. Чтобы быстрее перейти к редактированию меню, вы можете нажать кнопку New Menu (

Создайте меню, содержащее единственную строку Test, при выборе которой открывается меню, содержащее три строки - Beep и Exit. Внешний вид меню во время разработки представлен на рисунке 2.22.

Рис. 2.22. Разработка меню приложения
Когда вы создаете новое меню верхнего уровня или определяете строки, входящие в это меню, на экране появляется диалоговое окно Menu Item Properties (рис. 2.23). В нем полностью описывается выбранный элемент меню. В поле Caption задается название меню или строки меню. В названии вы можете вставить символ &. Он будет означать, что символ, следующий за ним, можно будет использовать для быстрого выбора данного элемента меню. Например, если перед названием строки меню Beep поставить символ &, то во время работы приложения символ B будет отображаться с подчеркиванием, и строку Beep можно будет выбрать, нажав комбинацию клавиш <Alt+B>.

Рис. 2.23. Диалоговая панель Menu Item Properties
Как вы знаете, каждый элемент меню должен иметь уникальный идентификатор, однозначно его определяющий. Вы можете задать идентификатор в поле ID или выбрать его из списка предопределенных идентификаторов.
Редактор ресурсов сам предлагает вам название идентификатора, создавая его из названия главного меню и строки меню. Так например строке Beep меню Test по умолчанию будет присвоен идентификатор ID_TEST_BEEP.
В поле Prompt можно в нескольких словах описать данный пункт меню. Введенная вами строка будет записана в строковый ресурс. Ей будет присвоен тот же самый идентификатор, что и пункту меню. Приложение может использовать строку описания, чтобы предоставить пользователю краткую справку о меню.
|
Переключатель |
Описание |
|
Break |
Этот переключатель может принимать три различных значения - None, Column и Bar. Он позволяет задать структуру меню. По умолчанию выбрано значение None оно соответствует нормальному виду меню без деления на колонки. Если выбрать значение Column, тогда пункт меню будет размещен в новом столбце. Значение Bar соответствует Column, за исключением меню верхнего уровня. Если указать Bar для меню верхнего уровня, то новая колонка будет отделена от старой вертикальной линией |
|
Checked |
Если установить переключатель, то строка меню будет выделена символом ![]() |
|
Grayed |
Если включить переключатель Grayed, тогда пункт меню будет отображаться серым цветом и будет недоступен для выбора пользователем. Такая возможность удобна, если вам надо сделать недоступным какие-либо возможности приложения. Впоследствии приложение может сделать пункт меню доступным. Для этого надо вызвать соответствующий метод |
|
Help |
Если установить переключатель Help, тогда для него будет установлено выравнивание по правому краю. Обычно этот переключатель устанавливают для меню верхнего уровня, которое управляет справочной системной приложения |
|
Inactive |
Если включен переключатель Grayed, тогда переключатель недоступен. В противном случае вы можете установить переключатель Inactive. В этом случае пункт меню будет неактивен |
|
Pop-up |
Вы можете создавать многоуровневые меню. Если вы включите переключатель Pop-up, то данный пункт меню будет являться меню верхнего уровня, которое можно открыть. По умолчанию, все пункты главного меню имеют установленный переключатель Pop-up. Так как меню верхнего уровня служит только для объединения других пунктов меню, то оно не имеет идентификатора |
|
Separator |
Если переключатель установлен, тогда в меню вставляется разделитель. Для разделителя все остальные поля и переключатели диалоговой панели не используются |


Документ, который вы можете создать в приложении Single, можно убрать, только полностью закрыв приложение. Функция создания нового документа не работает. Когда вы выбираете из меню File строку New или нажимаете кнопку

Оказывается, когда пользователь выбирает из меню File строку New, вызывается виртуальный метод OnNewDocument, определенный в классе CDocument. Если вы не переопределите этот метод, то по умолчанию он вызывает метод DeleteContents, и далее помечает его как чистый (пустой). Вы можете переопределить метод OnNewDocument в своем классе документа, чтобы выполнить его инициализацию. Требуется, чтобы вы вызывали из переопределенного метода OnNewDocument, метод OnNewDocument, определенный в базовом классе CDocument.
Когда пользователь создает новый документ в приложении, построенном на основе однооконного интерфейса, то на самом деле используется старый документ. Новый объект класса, представляющего документ, не создается. Метод OnNewDocument должен удалить содержимое документа и выполнить повторную инициализацию существующего объекта класса документ.
Из этого следует, что нельзя выполнять инициализацию документа в конструкторе класса документа, так как конструктор будет вызван только один раз за время работы приложения. Более правильно использовать для этой цели метод OnNewDocument.
Для приложений, использующих многооконный интерфейс, процедура создания нового документа отличается. Каждый раз, когда пользователь создает новый документ, создается новый объект класса документ. Процесс создания нового документа мы опишем в следующей главе.
//////////////////////////////////////////////////////////////
// Метод DeleteContents
void CSingleDoc::DeleteContents()
{
// TODO:
// Очищаем документ, удаляя все элементы массива arrayFig.
// Метод RemoveAll определен в классе CArray
arrayFig.RemoveAll();
// Вызываем метод DeleteContents базового класса CDocument
CDocument::DeleteContents();
}
Из любой страницы ClassWizard можно добавить в приложение новый класс, созданный на основе базовых классов. В качестве базового класса можно использовать классы, наследованные от класса CCmdTarget или класса CRecordset. Если требуется создать класс, не наследуемый от базовых классов CCmdTarget или CRecordset, использовать средства ClassWizard нельзя. Такие классы надо создавать вручную, непосредственно в текстовом редакторе.
Объекты порожденные от класса CCmdTarget могут обрабатывать сообщения Windows и команды, поступающие от меню, кнопок, акселераторов. Класс CCmdTarget и другие наследованные от него классы имеют таблицу сообщений (Message map) - набор макрокоманд, позволяющий сопоставить сообщениям Windows и командам методы класса
Чтобы создать класс, нажмите кнопку Add Class из любой страницы главной диалоговой панели ClassWizard. Откроется временное меню, содержащее три строки: New, From a file, From an OLE TypeLib. Для создания нового класса выберите из этого меню строку New. Если вы уже имеете исходные тексты класса и их просто требуется подключить к проекту, выберите из меню строку From a file. Последняя строка меню From an OLE TypeLib используется для подключения классов из библиотеки OLE.
Когда вы создаете новый класс, на экране появляется диалоговая панель Create New Class. В поле Name введите имя создаваемого класса. Рекомендуется начинать названия классов с символа “C”. Для создаваемого класса организуются два файла реализации класса, имеющие расширения CPP и H. В них будут помещаться объявления класса, а также определения его методов и данных. Имя файлов реализации отображается в левой части группы File. По умолчанию файлы реализации имеют имена, соответствующие имени класса. Однако их можно изменить, воспользовавшись кнопкой Change из группы File.
Теперь выберите из списка Base Class имя базового класса. Список Base Class достаточно велик. В нем содержатся не только основополагающие классы типа CCmdTarget, CDialog, CDocument, CFrameWnd, CView, CWinThread, CWnd. Список базовых классов включает классы большинства органов управления, например CAnimateCtrl, CButton, CColorDialog, CComboBox, CDragListBox, CEdit, CEditView, CFileDialog, CFontDialog, CHeaderCtrl, CHotKeyCtrl, CListBox, CListCtrl, CListView, CProgressCtrl, CStatic и многие многие другие. Доступны также базовые классы, предназначенные для работы с базами данных: CDaoRecordSet, CDaoRecordView, CRecordset, CRecordView, классы обеспечивающие технологию OLE: COleDocument, COleLinkingDoc, COleServerDoc.
Во второй главе книги мы рассматривали приложение MFDialog, которое не имеет главного окна. Вместо окна это приложение использует обыкновенную диалоговую панель. Сейчас мы расскажем вам как создать приложение, подобное MFDialog, не набрав ни одной строки текста программы. Для этого мы будем использовать средства MFC AppWizard и ClassWizard.
Выберите из меню File строку New. На экране появится диалоговая панель New, содержащая меню. Выберите из него тип объекта, который надо создать. Для создания нового проекта выберите из этого меню строку Project Workspace. Теперь на экране откроется диалоговая панель New Project Workspace, показанная нами на рисунке 4.1.

Рис. 4.1. Диалоговая панель New Project Workspace
Из списка Type выберите тип приложения, которое вы желаете создать. В следующей таблице перечислены типы приложений, которые вы можете выбрать.
| Тип приложения | Описание | ||
| MFC AppWizard (exe) | Приложение, создаваемое с использованием библиотеки классов MFC. С помощью AppWizard вы можете автоматически создать основные классы необходимые для приложения | ||
| MFC AppWizard (dll) | Библиотека динамической компоновки - DLL, создаваемая с помощью библиотеки классов MFC. AppWizard позволяет автоматически создать все основные файлы, необходимые для DLL | ||
| OLE ControlWizard | Органы управления OLE, созданные с использованием библиотеки классов MFC. Компилятор автоматически создает базовый набор файлов для проекта этого типа | ||
| Application | Приложение, созданное на основе библиотеки классов MFC или с использованием только вызовов функций программного интерфейса Windows | ||
| Dynamic-Link Library | Библиотека динамической компоновки, созданная с использованием только вызовов функций программного интерфейса Windows | ||
| Console Application | Приложение, разработанное с использованием функций консольного ввода/вывода. Этот тип приложений можно использовать для создания небольших программ, работающих в пакетном режиме | ||
| Static Library | Библиотека функций | ||
| Makefile | Предоставляет дополнительные возможности для использования MAKE-файла | ||
| Custom AppWizard | Позволяет создать собственный “волшебник” Custom AppWizard, который можно будет использовать для разработки шаблонов приложений с заданными вами свойствами |




|
Имя поля |
Описание |
|
Class name |
Имя класса приложения, выбранное вами из списка. Вы можете изменить его по вашему усмотрению или оставить как есть |
|
Base class |
Имя базового класса MFC, из которого наследуется класс выбранный из списка Class name. Для ряда классов базовый класс можно изменить. Более подробно об этом мы расскажем позже |
|
Header file, Implementation file |
Эти два поля определяют названия включаемого файла, в котором описан класс, и файла, содержащего исходный код методов класса. Вы можете изменить их по своему усмотрению |

В состав MFC входят классы, предназначенные для организации двунаправленных списков указателей, строк, состоящих из объектов CString, указателей на объекты класса CObject. В MFC версии 4.0 добавлен шаблон класса списка CList. С помощью этого шаблона можно создавать списки, состоящие из любых объектов.
Списковый класс имеет методы для вставки и удаления элементов в начало, конец и середину списка. Вы можете получить предыдущий и следующий элемент из списка.
Ниже представлен прототип шаблона CList.
template <class TYPE, class ARG_TYPE>
class CList : public CObject
Конечно, вы можете продолжать набирать исходные тексты приложения вручную непосредственно в текстовом редакторе. Но во многих случаях среда VIsual C++ может оказать вам значительную помощь. Одним из средств, способных оказать вам такую помощь уже сейчас, является ClassView.
Используя ClassView можно быстро добавлять к классам новые элементы, просматривать структуру наследования классов и выполнять другие полезные операции. После того как вы разберетесь с основными принципами построения приложений Windows с использованием классов MFC, мы продолжим изучение вспомогательных средств VIsual C++. В следующих главах книги мы рассмотрим средства автоматизированного проектирования приложений MFC AppWizard и средство для разработки классов ClassWizard.
А сейчас приступим к описанию возможностей ClassView. Выберите из списка ClassView название класса, который вы желаете просмотреть или изменить, и нажмите правую клавишу мыши. На экране появится временное меню, представленное на рисунке 2.14.

Рис. 2.14. Временное меню класса
Строки этого меню позволяют выполнять над классами все основные действия. Строка Go to Definition позволяет просмотреть в окне редактирования исходный текст класса. Вы можете редактировать класс непосредственно, но для добавления в класс новых методов и данных удобнее пользоваться строками Add Function и Add Variable.
Разработка приложения не заканчивается, когда MFC AppWizard создаст для вас исходные файлы проекта. Теперь вы должны добавить к приложению собственный программный код, выполняющий основные функции приложения. Среда Microsoft Visual C++ версии 4.0 позволяет максимально облегчить дальнейшую разработку приложения. Для этого предназначены такие средства как ClassView и ClassWizard. Они позволяют с минимальными усилиями добавлять в классы новые методы и данные, быстро высвечивать в окне редактирования интересующие вас объявления и определения классов, методов, данных.
Мы уже изучили возможности ClassView, предназначенные для работы с классами в разделе “Средства ClassView” главы “Введение в MFC”. По сравнению с ClassView, “волшебник” ClassWizard предоставляет более полные услуги. Он позволяет не только добавлять к классу новые методы и данные. Вы можете использовать ClassWizard, чтобы добавить к классу новый метод, служащий для обработки сообщений, переменную, предназначенную для обмена информацией с полями диалоговой панели.
Запустить ClassWizard очень просто, для этого можно нажать кнопку


Рис. 4.10. Главная панель ClassWizard
Главная панель ClassWizard содержит пять страниц - Message Maps, Member Variables, OLE Automation, OLE Events и Class Info. Страница Message Maps позволяет просмотреть сообщения, вырабатываемые объектами, и создать методы для их обработки. Вторая страница Member Variables позволяет управлять данными, записанными в классе. Следующие две страницы OLE Automation и OLE Events отвечают за поддержку вашим приложением технологии OLE. Эти страницы будут рассмотрены позже. Последняя страница Class Info позволяет получить различную информацию о классе.
Когда вы просматриваете исходный текст приложения, в верхней части окна редактора может отображаться панель WizardBar (рис. 4.11). Органы управления этой панели позволяют получить быстрый доступ к некоторым возможностям ClassWizard. WizardBar позволяет управлять методами, обрабатывающими сообщения от органов управления.

Рис. 4.11. Панель WizardBar
В языке Си++ вы можете определить ссылку на объект - переменную или объект класса. Ссылка содержит адрес объекта, но вы можете использовать ее, как будто она представляет сам объект. Для объявления ссылки используется оператор &.
В следующей программе мы определили переменную iVar типа int и ссылку iReferenceVar на нее. Затем мы отображаем и изменяем значение переменной iVar используя ее имя и ссылку.
// Включаемый файл для потокового ввода/вывода
#include <iostream.h>
void main(void)
{
// Определяем переменную iVar
int iVar = 10;
// Определяем ссылку iReferenceVar на переменную iVar
int& iReferenceVar = iVar;
// Отображаем значение переменной и ссылки
cout << "iVar = " << iVar << "; iReferenceVar = " <<
iReferenceVar << '\n';
// Изменяем значение переменной iVar пользуясь ссылкой
iReferenceVar = 20;
// Отображаем значение переменной и ссылки
cout << "iVar = " << iVar << "; iReferenceVar = " <<
iReferenceVar << '\n';
}
Вы можете использовать ссылки для передачи параметров функциям. При этом фактически вы передаете функции указатель на объект, представленный ссылкой. Внутри функции вы можете работать с ссылкой как с самим объектом, а не как с указателем.
Функция может не только принимать ссылки в качестве своих параметров, она также может возвращать ссылку. Такую функцию можно привести в левой части оператора присваивания.
Подавляющее большинство приложений, созданных на основе MFC, использует ряд стандартных командных сообщений, как правило соответствующих элементам меню или кнопкам панели управления. К ним относятся командные сообщения для завершения работы приложения, создания нового документа, открытия документа, записанного на диске, сохранения документа на диске, вызова справочной системы, управления текстовым редактором и т. д. За каждым таким командным сообщением зарезервирован отдельный идентификатор.
MFC обеспечивает различный уровень обработки стандартных командных сообщений, начиная от простого резервирования идентификатора и кончая полной его обработкой.
Элемент меню или кнопка панели управления приложения имеет тот же идентификатор, что и командное сообщение. Просмотрите список идентификаторов меню приложения Single. В нем вы найдете многие стандартные команды, описанные ниже.
Ниже коротко описаны наиболее важные стандартные командные сообщения.
Вы можете объявить некоторые методы класса статическими методами. Для этого вы должны воспользоваться ключевым словом static. Статические методы не принимают параметр this. На использование статических методов накладывается ряд ограничений.
Статические методы могут непосредственно обращаться только к статическим членам класса.
Статический метод не может быть объявлен как виртуальный метод.
Вы не можете определить нестатический метод с тем же именем и тем же набором параметров, что и статический метод класса.
Статические методы имеют одну интересную особенность - вы можете вызывать их даже без создания объектов класса. Чтобы вызвать из программы статический метод, вы должны указать его полное имя, включая имя класса.
Ниже представлен класс Circle, в котором определена статический метод GetPi. Он используется для получения значения статического элемента класса fPi.
class Circle
{
public:
static void GetPi()
{ return fPi; }
private:
static float fPi;
};
float Circle::fPi = 3.1415;
Вы можете вызвать метод GetPi следующим образом:
float fNumber;
fNumber = Circle::GetPi();
Обратите внимание, что объект класса Circle не создается.
Понятие структуры в языке Си++ значительно расширено. Структура в Си++ обладает всеми возможностями классов. В структуры Си++ можно включать не только элементы данных, но и методы. Вы можете наследовать от структур новые структуры, точно также как вы наследуете новые классы от базовых классов.
Различие между структурами и обычными классами заключается только в управлении доступом к их элементам. Так, если элементы класса по умолчанию объявлены как private, то все элементы структуры по умолчанию объявлены как public.
Ниже мы привели пример объявления структуры StructData и класса ClassData, которые содержат одинаковые элементы с одинаковыми правами доступа к ним. Фактически, структура StructData и класс ClassData совершенно равнозначны.
//====================================================
// Класс ClassData
class ClassData
{
int iPrivateValue;
public:
int iPublicValue;
};
//====================================================
// Структура StructData
struct StructData
{
int iPublicValue;
private:
int iPrivateValue;
};
Еще одно различие между структурами и классами проявляется в разграничении доступа к элементам базового класса (см. раздел “Разграничение доступа к элементам базового класса”). Если вы наследуете новый класс от базового класса и не указываете спецификатор доступа, по умолчанию используется спецификатор private. Когда же вы наследуете от базового класса структуру, по умолчанию используется спецификатор public.
Чтобы ускорить доступ к строкам меню приложения, MFC AppWizard добавляет в файл ресурсов таблицу акселераторов. Когда пользователь нажимает комбинацию клавиш, представленную в таблице акселераторов, приложению поступает командное сообщение с соответствующим идентификатором.
//////////////////////////////////////////////////////////////
// Таблица акселераторов
IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE
BEGIN
"N", ID_FILE_NEW, VIRTKEY,CONTROL
"O", ID_FILE_OPEN, VIRTKEY,CONTROL
"S", ID_FILE_SAVE, VIRTKEY,CONTROL
"P", ID_FILE_PRINT, VIRTKEY,CONTROL
"Z", ID_EDIT_UNDO, VIRTKEY,CONTROL
"X", ID_EDIT_CUT, VIRTKEY,CONTROL
"C", ID_EDIT_COPY, VIRTKEY,CONTROL
"V", ID_EDIT_PASTE, VIRTKEY,CONTROL
VK_BACK, ID_EDIT_UNDO, VIRTKEY,ALT
VK_DELETE, ID_EDIT_CUT, VIRTKEY,SHIFT
VK_INSERT, ID_EDIT_COPY, VIRTKEY,CONTROL
VK_INSERT, ID_EDIT_PASTE, VIRTKEY,SHIFT
VK_F6, ID_NEXT_PANE, VIRTKEY
VK_F6, ID_PREV_PANE, VIRTKEY,SHIFT
END
В библиотеке классов MFC для обработки сообщений используется специальный механизм, который получил название Message Map - таблица сообщений.
Таблица сообщений состоит из набора специальных макрокоманд, ограниченных макрокомандами BEGIN_MESSAGE_MAP и END_MESSAGE_MAP. Между ними расположены макрокоманды, отвечающие за обработку отдельных сообщений.
Макрокоманда BEGIN_MESSAGE_MAP представляет собой заголовок таблицы сообщений. Она имеет два параметра. Первый параметр содержит имя класса таблицы сообщений. Второй параметр указывает его базовый класс.
Если в таблице сообщений класса отсутствует обработчик для сообщения, оно передается для обработки базовому классу, указанному вторым параметром макрокоманды BEGIN_MESSAGE_MAP. Если таблица сообщений базового класса также не содержит обработчик этого сообщения, оно передается следующему базовому классу и т. д.
В том случае если ни один из базовых классов не может обработать сообщение, выполняется обработка по умолчанию, зависящая от типа сообщения.
Стандартные сообщения Windows обрабатываются функцией “default window procedure”
Командные сообщения передаются по цепочке следующему объекту, который может обработать командное сообщение. Более подробно мы расскажем об этой цепочке в главах “Однооконный интерфейс” и “Многооконный интерфейс”
В библиотеке MFC определены несколько макрокоманд, отвечающих за обработку сообщений. Их названия представлены в следующей таблице.
| Макрокоманда | Устанавливает методы для обработки сообщений | ||
| ON_WM_<name> | Стандартных сообщений операционной системы Windows | ||
| ON_REGISTERED_MESSAGE | Зарегистрированные сообщения операционной системы Windows | ||
| ON_MESSAGE | Сообщений, определенных пользователем | ||
| ON_COMMAND, ON_COMMAND_RANGE | Командных сообщений | ||
| ON_UPDATE_COMMAND_UI, ON_UPDATE_COMMAND_UI_RANGE | Сообщений, предназначенных для обновления пользовательского интерфейса | ||
| ON_<name>, ON_CONTROL_RANGE | Сообщений от органов управления |
Перечисленные в таблице макрокоманды имеют различное количество параметров в зависимости от типа обрабатываемых ими сообщений.
Таблица сообщений класса CDialogApp состоит из макрокоманд BEGIN_MESSAGE_MAP и END_MESSAGE_MAP. Между ними расположены макрокоманды, определяющие сообщения, обрабатываемые данным классом. В таблице определено только одно командное сообщение, имеющее идентификатор ID_HELP. Для его обработки вызывается метод OnHelp базового класса CWinApp.
Необработанные сообщения передаются базовому классу CWinApp, так как он указан во втором параметре макрокоманды BEGIN_MESSAGE_MAP.
//////////////////////////////////////////////////////////////
// Таблица сообщений класса CDialogApp
BEGIN_MESSAGE_MAP(CDialogApp, CWinApp)
//{{AFX_MSG_MAP(CDialogApp)
// ClassWizard размещает в данном блоке макрокоманды для
// обработки сообщений. Не изменяйте содержимое этого блока
//}}AFX_MSG
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()
Обратите внимание, что внутри таблицы сообщений расположены две макрокоманды AFX_MSG, помещенные за знаками комментария. Сразу после создания приложения между ними нет ни одной макрокоманды. Когда вы будете создавать обработчики сообщений при помощи ClassWizard, он будет располагать новые обработчики между этими комментариями. Не рекомендуется вручную вносить изменения в код, расположенный в блоке AFX_MSG. Используйте для этого средства ClassWizard.
Если вам требуется добавить новый обработчик в таблицу сообщений, без использования ClassWizard, расположите его после блока AFX_MSG и до макрокоманды END_MESSAGE_MAP.
Приложение Dialog содержит еще одну таблицу сообщений, принадлежащую классу диалоговой панели приложения. Мы рассмотрим эту таблицу позже.
Непосредственно после таблицы сообщений главного класса приложения расположено определение конструктора CDialogApp. Этот конструктор вызывается в момент создания объекта приложения. Конструктор, созданный MFC AppWizard, пустой и не выполняет никаких дополнительных действий. Для инициализации приложения используются методы InitInstance и InitApplication.
Файл DialogDlg.cpp содержит таблицу сообщений класса CDialogDlg. Таблица включает три макрокоманды. Как видите, они расположены в блоке AFX_MSG_MAP, поэтому для управления ими используется ClassWizard.
BEGIN_MESSAGE_MAP(CDialogDlg, CDialog)
//{{AFX_MSG_MAP(CDialogDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
При помощи ClassWizard вы можете обнаружить, что макрокоманды выполняют обработку сообщений WM_SYSCOMMAND, WM_PAINT, WM_QUERYDRAGICON, вызывая для этого методы OnSysCommand, OnPaint и OnQueryDragIcon.
Класс CMainFrame может получать и обрабатывать сообщения, поэтому в определении класса указана макрокоманда DECLARE_MESSAGE_MAP, а в файле реализации класса MainFrm.cpp, расположена таблица сообщений.
// Таблица сообщений класса CMainFrame
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
Изначально в таблице сообщений расположена единственная макрокоманда ON_WM_CREATE. Эта макрокоманда устанавливает для обработки сообщения WM_CREATE метод OnCreate. Как вы знаете, сообщение WM_CREATE передается функции окна сразу после его создания, но до того, как окно появится на экране.
Обратите внимание, что в последней строке определения класса CSingleApp расположена макрокоманда DECLARE_MESSAGE_MAP. Загадочная макрокоманда DECLARE_MESSAGE_MAP определена в файле afxwin.h следующим образом:
#define DECLARE_MESSAGE_MAP() \
private: \
static const AFX_MSGMAP_ENTRY _messageEntries[]; \
protected: \
static AFX_DATA const AFX_MSGMAP messageMap; \
virtual const AFX_MSGMAP* GetMessageMap() const; \
Таким образом, DECLARE_MESSAGE_MAP не является расширением языка Си++, а просто добавляет к вашему классу несколько новых элементов.
Так как в классе CSingleApp расположена макрокоманда DECLARE_MESSAGE_MAP, то он может обрабатывать сообщения и имеет таблицу сообщений. Таблица сообщений класса CSingleApp расположена в файле реализации Single.cpp.
//////////////////////////////////////////////////////////////
// Таблица сообщений класса CSingleApp
BEGIN_MESSAGE_MAP(CSingleApp, CWinApp)
//{{AFX_MSG_MAP(CSingleApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
//}}AFX_MSG_MAP
// Стандартные команды для работы с документами
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
// Стандартная команда выбора принтера
ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
Кроме команды для обработки командного сообщения ID_APP_ABOUT, расположенного в блоке AFX_MSG_MAP, таблица сообщений содержит еще три макрокоманды, предназначенные для обработки командных сообщений с идентификаторами ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_PRINT_SETUP.
Командные сообщения ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_PRINT_SETUP поступают, когда пользователь выбирает из меню приложения строки с соответствующими идентификаторами. Для обработки этих командных сообщений вызываются методы класса CWinApp.
Макрокоманда IMPLEMENT_DYNCREATE размещается в файле реализации класса. Для класса CSingleDoc этот файл называется SingleDoc.cpp. Обычно MFC AppWizard размещает макрокоманду IMPLEMENT_DYNCREATE непосредственно перед таблицей сообщений класса (если конечно данные класс обрабатывает сообщения).
// Макрокоманда необходима для динамического создания объектов
// CSingleDoc
IMPLEMENT_DYNCREATE(CSingleDoc, CDocument)
// Таблица сообщений класса CSingleDoc
BEGIN_MESSAGE_MAP(CSingleDoc, CDocument)
//{{AFX_MSG_MAP(CSingleDoc)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
Сазу после создания проекта таблица сообщений класса CSingleDoc не содержит обработчиков сообщений. Когда вы продолжите разработку приложения, вы будете добавлять обработчики различных сообщений к классу CSingleDoc и другим классам приложения. Для добавления новых обработчикоов сообщений, а также для внесения других изменений в классы, следует использовать ClassWizard.
Таблица сообщений класса CSingleView располагается в файле SingleView.cpp. Непосредственно перед ней находится макрокоманда IMPLEMENT_DYNCREATE.
// Объекты класса CSingleView могут создаваться динамически
IMPLEMENT_DYNCREATE(CSingleView, CView)
// Таблица сообщений класса CSingleView
BEGIN_MESSAGE_MAP(CSingleView, CView)
//{{AFX_MSG_MAP(CSingleView)
//}}AFX_MSG_MAP
// Стандартные команды предназначенные для печати документа
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
В таблице текстовых строк проекта Dialog определена только одна текстовая строка &About Dialog..., имеющая идентификатор IDS_ABOUTBOX. Эта строка содержит текст нового элемента, который будет добавлен к системному меню главной диалоговой панели приложения. Если пользователь выберет эту строку меню, приложение выведет на экран небольшую диалоговую панель, с краткой информацией о приложении.
//////////////////////////////////////////////////////////////
// Таблица строк
STRINGTABLE DISCARDABLE
BEGIN
IDS_ABOUTBOX "&About Dialog..."
END
Если вызывается исключение, для которого отсутствует обработчик и не определен универсальный обработчик исключений всех типов, тогда вызывается функция terminate из стандартной библиотеки. Она вызывает функцию abort, завершающую работу программы.
Вы можете определить собственную функцию, которая будет вызываться перед аварийным завершением программы. Для этого вы должны вызвать функцию set_terminate, указав ей в качестве параметра имя вашей функции. Если вы воспользуетесь функцией set_terminate несколько раз, то будет вызываться только функция, указанная в последнем вызове set_terminate.
#include <eh.h>
#include <iostream.h>
#include <process.h>
void FastExit(void);
int main()
{
// Устанавливаем функцию term_func
set_terminate(FastExit);
try
{
// ...
// Вызываем исключение типа int
throw (int) 323;
// ...
}
// Определяем обработчик типа char. Обработчик исключений
// типа int и универсальный обработчик не определены
catch(char)
{
cout << "Exception " << endl;
}
return 0;
}
// Определение функции FastExit
void FastExit()
{
cout << "Exception handler not found" << endl;
exit(-1);
}
Среда Visual C++ версии 4.0 позволяет запретить или разрешить обработку исключений языка Си++. Для управления исключениями выберите из меню Build строку Settings. На экране появится диалоговая панель Project Settings, в которой определяются все режимы работы. Выберите страницу C/C++. Затем из списка Category выберите строку C++ Language. Чтобы включить обработку исключительных ситуаций установите переключатель Enable exception handling.
В одном блоке try можно вызывать исключения разных типов. В этом случае после блока try должны следовать обработчики для исключений каждого типа. Вы можете определить обработчик, обслуживающий исключения всех типов. Для этого вместо типа в операторе catch надо указать три точки:
catch(...)
{
...
}
Исключения в языке Си++ могут быть различного типа, в том числе они могут быть объектами классов. Вы можете определить несколько обработчиков исключений различного типа. В этом случае исключение будет обрабатывать обработчик соответствующего типа.
Во всех приложениях, созданных с использованием MFC AppWizard, определен специальный ресурс, содержащий различные сведения о версии приложения (4.8). Приложение Dialog также содержит такой ресурс, который имеет идентификатор VS_VERSION_INFO.

Рис. 4.8. Информация о версии приложения
Вы можете внести изменения в этот ресурс, однако имеет смысл делать это только на конечной стадии разработки приложения. Поэтому мы не станем сейчас подробно останавливаться на описании этого ресурса.
Как и каждое приложение, созданное средствами MFC AppWizard, приложение Single включает ресурс, описывающий версию приложения. В этом ресурсе содержится информация о приложении и ее версии, данные о фирме-разработчике, авторские права.
Приложения, как правило, имеют только один ресурс, содержащий данные о версии, и который имеет имя VS_VERSION_INFO. Приложение может получить данные из ресурса, описывающего версию приложения. Для этого можно воспользоваться функциями GetFileVersionInfo и VerQueryValue.
Сейчас мы не станем подробно останавливаться на этом ресурсе. Большинство приложений не нуждается в определении данного ресурса, тем более на начальном этапе разработки. Поэтому мы продолжим изучения исходного текста самого приложения.
Методы базового класса могут быть переопределены в порожденных классах. Если вы создадите объект порожденного класса и вызовете для него переопределенный метод, то будет вызван именно метод порожденного класса, а не соответствующий метод базового класса. Однако, если вы вызовете переопределенный метод для объекта порожденного класса, используя указатель или ссылку на объект базового класса, будет вызван именно метод базового класса. Иными словами метод вызывается в соответствии с классом указателя на объект, а не с классом самого объекта.
В Си++ вы можете указать, что некоторые методы базового класса, которые будут переопределены в порожденных классах, являются виртуальными. Для этого достаточно указать перед описанием метода ключевое слово virtual. Статический метод не может быть виртуальным. Методы, объявленные в базовом классе виртуальными считаются виртуальными и в порожденных классах.
Если вы переопределите в порожденном классе виртуальный метод, и создадите объект этого класса, то переопределенный метод будет использоваться вне зависимости от того, как он был вызван. При вызове переопределенного метода играет роль только класс объекта для которого вызывается метод.
Все сказанное нами не означает, что никак нельзя вызвать виртуальный метод базового класса, если он был переопределен. Виртуальный метод базового класса можно вызвать, если указать его полное имя, включая имя базового класса.
Виртуальный метод базового класса можно оставить без изменения и не переопределять в порожденном классе. В этом случае, он будет работать как обычный не виртуальный метод.
Следующая программа демонстрирует разницу между виртуальными и невиртуальными методами класса. В базовом классе Figure определены два метода PrintName и PrintDimention, причем метод PrintName определен как виртуальный. От класса Figure наследуется класс Rectangle, в котором методы PrintName и PrintDimention переопределяются.
В программе создается объект класса Rectangle, а затем несколько раз вызываются методы PrintName и PrintDimention. В зависимости от того, как вызывается метод, будет работать метод, определенный в классе Figure или Rectangle.
Виртуальный метод GetRuntimeClass возвращает указатель на структуру CRuntimeClass, описывающую класс объекта, для которого метод был вызван:
virtual CRuntimeClass* GetRuntimeClass() const;
Для каждого класса, наследованного от CObject поддерживается своя структура CRuntimeClass. Если вы желаете использовать метод GetRuntimeClass в своем классе, наследованном от CObject, вы должны поместить в реализации класса макрокоманду IMPLEMENT_DYNAMIC или IMPLEMENT_SERIAL.
Структура CRuntimeClass содержит различную информацию о классе. Ниже перечислены несколько основные полей этой структуры.
| Поле структуры CRuntimeClass | Описание | ||
| const char* m_pszClassName | Указатель на строку, закрытую двоичным нулем, в которой расположено имя класса | ||
| int m_nObjectSize | Размер объектов класса | ||
| WORD m_wSchema | Номер схемы (schema number) класса. Используется при автоматическом сохранении и восстановлении объектов класса в файле. Если объекты класса не могут быть сохранены и восстановлены (в объявлении класса отсутствует макрокоманда IMPLEMENT_SERIAL), m_wSchema содержит значение -1 | ||
| void (*m_pfnConstruct) (void* p) | Указатель на конструктор класса, используемый по умолчанию. Этот конструктор не имеет параметров | ||
| CRuntimeClass* m_pBaseClass | Указатель на структуру CRuntimeClass, содержащую аналогичную информацию о базовом классе |
Кроме перечисленных элементов структуры, она содержит метод CreateObject. Этот метод позволяет динамически создать объект соответствующего класса уже во время работы приложения. Если объект класса не создан, метод возвращает значение NULL.
CObject* CreateObject();
Виртуальный метод Serialize вызывается, когда надо сохранить или восстановить объект класса из файла на диске. Вы должны переопределить этот метод в своем классе, чтобы сохранить или восстановить его элементы. Переопределенный метод Serialize должен вызывать метод Serialize базового класса:
virtual void Serialize(CArchive& ar);
throw(CMemoryException);
throw(CArchiveException);
throw(CFileException);
Ниже прототипа метода Serialize мы указали исключения, которые могут быть им вызваны. Более подробно об исключениях вы можете прочитать в разделе “Исключения - класс CException” данной главы.
ClassWizard позволяет включать в класс не только новые методы, но также и элементы данных, связанные с полями диалоговых панелей, форм просмотра и форм для просмотра записей баз данных и полей наборов записей. ClassWizard, использует специальные процедуры, чтобы привязать созданные им элементы данных класса к полям диалоговых панелей. Эти процедуры носят название обмен данными диалоговой панели и проверка данных диалоговой панели (Dialog Data Exchange and Dialog Data Validation - DDX/DDV). Чтобы привязать поля из наборов записей к переменным, используются процедуры обмена данными с полями записей - (Record Field Exchange - RFX).
Процедуры DDX/DDV и RFX значительно упрощают программисту работу с диалоговыми панелями. Они позволяют связать поля диалоговых панелей и переменные. Когда пользователь редактирует поля диалоговых панелей, процедуры DDV проверяют введенные значение и блокируют ввод запрещенных значений. Затем процедуры DDX автоматически копируют содержимое полей диалоговых панелей в привязанные к ним элементы данных класса. И наоборот, когда приложение изменяет элементы данных класса, привязанные к полям диалоговой панели, процедуры DDX могут сразу отобразить новые значения полей на экране компьютера.
Базовые сведения о процедурах обмена данных и проверке введенных значений мы привели в разделе “Диалоговая панель” главы “Введение в MFC”. А теперь мы рассмотрим средства, предоставляемые ClassWizard.
Откройте ClassWizard и выберите из списка Class name имя класса, к которому надо добавить новый элемент данных. Теперь из списка Control IDs выберите идентификатор органа управления диалоговой панели, к которому надо привязать элемент данных класса. Если из списка Class name выбрать имя класса, не соответствующего диалоговой панели, форме просмотра, форме просмотра записей или набору записей, то список Control IDs остается пустым.
В столбце Type отображается тип элемента данных, а в столбце Member имя этого элемента данных. Если к органу управления не подключен элемент данных класса, то соответствующие позиции в столбцах Type и Member остаются незаполненными.
Очень удобно использовать ClassWizard для включения в состав класса новых методов. Вы можете добавлять к классу методы, служащие для обработки сообщений Windows и команд от объектов, а также методы, переопределяющие виртуальные методы базовых классов.
Выберите из списка Class name имя класса, к которому надо добавить новый метод. Теперь из списка Object IDs выберите идентификатор объекта, для которого надо создать метод. Если вам надо переопределить виртуальный метод базового класса, выберите из этого списка имя самого класса.
Обратите внимание на список Messages. Если вы выбрали в списке Object IDs идентификатор объекта, то в списке Messages отображаются сообщения, которые этот объект может вырабатывать. Например, если в качестве объекта фигурирует строка меню, то в списке сообщений отображаются два сообщения - COMMAND и UPDATE_COMMAND_UI. Сообщение COMMAND передается, когда данная строка выбирается из меню, а сообщение UPDATE_COMMAND_UI - когда открывается меню, содержащее эту строку.
Выберите из списка Messages то сообщение, для которого надо создать обрабатывающий его метод и нажмите кнопку Add Function. Откроется диалоговая панель Add Member Function. В ней надо определить название нового метода. По умолчанию вам будет предложено название, построенное на основе имени объекта и имени самого сообщения. Нажмите кнопку OK. ClassWizard добавит описание нового метода в класс, а также сформирует шаблон для этого метода, учитывающий все особенности объекта и сообщения.
Название сообщения, для которого создан метод, или имя переопределенного метода из списка Messages выделяется жирным шрифтом. Название нового метода появится в списке методов класса - Member Function, расположенном в нижней части диалоговой панели ClassWizard. Перед названием методов размещаются символы




Чтобы перейти к редактированию метода класса, установите на его имя указатель мыши и сделайте двойной щелчок левой кнопкой мыши или нажмите кнопку Edit Code. Откроется окно редактора и курсор встанет непосредственно в начало определения метода.
ClassWizard не только позволяет добавить в класс новые методы, но и удалить их. ClassWizard самостоятельно удалит объявление метода из класса. Чтобы удалить метод, выберите его название из списка Member Function и нажмите кнопку Delete Function.
В некоторых случаях более удобно и эффективно выполнять подстановку тела функции вместо ее вызова. Непосредственная подстановка тела функции позволит сэкономить время процессора на вызове функции. В языке Си этого можно достичь при помощи директивы препроцессора #define. Однако неправильное использование директивы может стать причиной ошибок.
Си++ предусматривает специальный механизм для встраивания функций. Чтобы указать компилятору, что данную функцию необходимо встраивать, перед ее объявлением или определением надо указать ключевое слово inline:
inline unsigned int Invert(unsigned int number) {
return (~number);
}
На сегодня существует более десятка версий библиотеки MFC. Практически каждая новая версия среды Microsoft Visual C++ (MSVC) поставляется с обновленной версией библиотеки MFC, в которой исправлены обнаруженные ошибки и добавлены новые классы.
Все версии библиотеки MFC можно разделить на две группы. К первой относятся 16-разрядные версии MFC, предназначенные для операционных систем Windows 3.1 и 3.11. Вторая группа включает версии MFC, предназначенные для 32-разрядных операционных систем Windows NT и Windows 95. В следующей таблице перечислены все основные версии Microsoft Visual C++ и соответствующие им версии MFC.
| Среда разработки | Версия MFC | Разрядность | |||
| Microsoft C/C++ версии 7.0 | 1.0 | 16 | |||
| MSVC 1.0 | 2.0 | 16 | |||
| MSVC 1.1 | 2.1 | 32 | |||
| MSVC 1.5 | 2.5 | 16 | |||
| MSVC 2.0 | 2.51 | 16 | |||
| MSVC 2.1 | 2.52 | 16 | |||
| MSVC 2.2 | 2.52b | 16 | |||
| MSVC 4.0 | 2.5c | 16 | |||
| MSVC 2.0 | 3.0 | 32 | |||
| MSVC 2.1 | 3.1 | 32 | |||
| MSVC 2.2 | 3.2 | 32 | |||
| MSVC 4.0 | 4.0 | 32 | |||
| MSVC 4.1 | 4.1 | 32 |
Вы легко можете определить версию библиотеки MFC, установленной на вашем компьютере. Для этого достаточно просмотреть включаемый файл afxver_.h, расположенный в каталоге include библиотеки MFC. В одной из первых строк этого файла определена константа _MFC_VER, содержащая версию MFC:
// Microsoft Foundation Classes версии 4.00
#define _MFC_VER 0x0400
Мы будем рассматривать библиотеку MFC версий 3.0, 4.0 и 4.1, однако приведенная информация верна и для других версий MFC. В тех случаях, когда эти версии имеют существенные отличия мы будем на это специально указывать.
Как вы знаете, операторы << и >> выполняют сдвиг числового значения влево и вправо на опеределенное число бит. В программах, приведенных в нашей книге, эти операторы также используются для ввода информации с клавиатуры и вывода на экран.
Если с левой стороны от оператора << расположен символ cout, то этот оператор осуществляет вывод на экран информации, указанной справа от оператора. Форма, в которой выполняется вывод на экран, зависит от типа выводимого значения. Используя оператор <<, вы можете отображать на экране текстовые строки, а также значения переменных различных типов. В качестве левого параметра оператора << можно использовать не только cout, но также результат работы предыдущего оператора <<. Это позволяет строить цепочки из операторов <<. Чтобы перейти к отображению следующей строки, вы можете передать cout значение \n.
Так, например, следующий фрагмент кода отображает на экране значения переменных iInt, cChar и szString с соответствующими комментариями:
cout << “Значение переменной iInt = ”;
cout << iInt;
cout << “\n”;
cout << “Значение переменной cChar = ” << cChar << “\n”;
cout << “Строка szString = ” << szString << “\n”;
Оператор >> и символ inp предназначены для ввода данных. Они позволяют пользователю ввести с клавиатуры значение какой-либо переменной. Ниже мы привели пример, в котором для ввода целочисленного значения используется inp и оператор >>:
int iNum;
cout << “Введите целочисленное значение:”;
cin >> iNum;
Чтобы воспользоваться возможностями потокового ввода/вывода, необходимо включить в программу файл iostream.h.
Забегая вперед, скажем, что символы inp и outp, которые иногда называют потоками, представляют собой объекты специального класса, предназначенного для ввода и вывода информации. Операторы << и >> переопределены в этом классе и выполняют новые функции. О переопределении операторов вы можете прочитать в разделе “Перегрузка операторов”.
Вы, конечно, можете определить таблицу сообщений класса вручную, однако наиболее удобно воспользоваться для этой цели средствами ClassWizard. ClassWizard не только позволит в удобной форме выбрать сообщения, которые должен обрабатывать ваш класс. Он включит в состав класса соответствующие методы-обработчики. Вам останется только вставить в них необходимый код. К сожалению использовать все возможности ClassWizard можно только в том случае, если приложение создано с использованием средств автоматизированного программирования MFC AppWizard.
Однако ClassWizard не всесилен. Так он не позволяет определить один метод для обработки нескольких сообщений. Как вы уже знаете, для этих целей предназначены макрокоманды ON_COMMAND_RANGE и ON_CONTROL_RANGE. Если вы решите воспользоваться этими макрокомандами, вам придется редактировать таблицу сообщений непосредственно, без использования ClassWizard.
Более подробно об использовании ClassWizard для создания обработчиков сообщений вы можете прочитать в разделе “Средства ClassWizard” главы “Приложение с главной диалоговой панелью”. А сейчас мы рассмотрим механизм обработки сообщений, используемый MFC, на примере нескольких приложений.
Еще одна интересная возможность, которая появляется у вас после перехода от Си к Си++, позволяет при определении функций задавать некоторые ее параметры по умолчанию. Вызывая такую функцию, можно не указывать параметры, заданные по умолчанию.
Если большинство вызовов функции выполняется с одинаковыми параметрами, это позволяет сократить текст программы, а главное, уменьшить возможность совершения ошибок во время набора параметров функции.
Параметры по умолчанию можно задать во время объявления функции или во время ее определения. По умолчанию задают только последние параметры функций:
int Summa(int first, int second, int third=0, int fourth=0) {
return(first + second + third + fourth);
}
Функцию Summa можно использовать для сложения четырех, трех или двух чисел. Если складываются два числа, то третий и четвертый параметр можно опустить:
void main() {
int value1 = 10, value2 = 20, value3 = 30, value4 = 40;
int result;
// Вызываем функцию с четырьмя параметрами
result = Summa(value1, value2, value3, value4);
print(“Сумма четырех чисел равна %d”, result);
// Вызываем функцию с тремя параметрами
result = Summa(value1, value2, value3);
print(“Сумма трех чисел равна %d”, result);
// Вызываем функцию с двумя параметрами,
// последний параметр задается по умолчанию
result = Summa(value1, value2);
print(“Сумма первых двух чисел равна %d”, result);
}
После того, как вы поработали с файлом, его надо закрыть. Класс CFile имеет для этого специальный метод Close:
virtual void Close();
throw(CFileException);
Метод закрывает файл. Если вы создали объект класса CFile и открыли файл, а затем объект удаляется, то связанный с ним файл закрывается автоматически с помощью деструктора.
Одна из задач, решаемых программистом при разработке приложений, которые могут создавать и редактировать документы различных типов, например текстовые или графические, заключается в том, чтобы предоставить пользователю возможность записать внутреннее представление документа в файл и восстановить его.
Чтобы облегчить программисту решение этой задачи, библиотека классов MFC определяет механизм записи и восстановления объектов (serialization). Поддержка механизма записи и восстановления объектов осуществляется средствами класса CObject.
Классы, наследованные от CObject также могут обеспечивать работу механизма записи и восстановления объектов. Для этого при объявлении класса надо указать макрокоманду DECLARE_SERIAL, а при определении - макрокоманду IMPLEMENT_SERIAL.
Макрокоманду DECLARE_SERIAL надо поместить перед описанием вашего класса во включаемом файле. Непосредственно после имени макрокоманды надо указать имя класса - class_name:
DECLARE_SERIAL(class_name)
Макрокоманду IMPLEMENT_SERIAL надо указать перед определением класса в файле исходного текста приложения, имеющего расширение CPP. Прототип макрокоманды IMPLEMENT_SERIAL представлен ниже:
IMPLEMENT_SERIAL(class_name, base_class_name, wSchema)
Параметр class_name определяет имя вашего класса, base_class_name - имя базового класса из которого непосредственно наследуется ваш класс. Последний параметр wSchema - это число типа UINT, определяющее версию программы. Если вы разработаете новую версию своего приложения и измените набор данных, которые необходимо записать в файл, измените параметр wSchema.
В классе должны быть определены специальные методы для записи и восстановления состояния объектов этого класса. Обычно эти методы сохраняют и восстанавливают элементы данных из класса. Таким образом, объекты класса сами отвечают за то, как они сохраняют и восстанавливают свое состояние.
Методы, сохраняющие и восстанавливающие состояние класса, взаимодействуют с объектом класса CArchive, который осуществляет непосредственную запись и чтение информации из файла на диске.
|
Константа |
Описание |
|
CArchive::load |
Данные считываются из файла на диске. Впоследствии они будут записаны в восстанавливаемый объект. В этом случае файл, определенный параметром pFile, должен быть открыт для чтения. |
|
CArchive::store |
Данные будут записываются в файл на диске. Файл, определенный параметром pFile должен быть открыт для записи. |
|
CArchive:: bNoFlushOnDelete |
Когда вызывается деструктор класса CArchive, он автоматически вызывает метод Flush для файла pFile. Если вы укажите этот флаг, то метод Flush вызван не будет. Чтобы предотвратить потерю данных, вы должны будете перед вызовом деструктора закрыть данный файл. |
Когда приложение желает сохранить состояние объекта данного класса в файле, оно вызывает для него метод Serialize. В качестве параметра этому методу передается указатель на объект класса CArchive, связанные с файлом, открытым на запись.
В этом случае реализация метода Serialize должна сохранить в файле все элементы данных, которые потом потребуется восстановить. Для этого необходимо воспользоваться оператором << или методами WriteString и Write, определенными в классе CArchive.
Оператор << можно использовать для записи в архивный файл переменных простых типов, например long, int, char и объектов других классов, которые наследованы от класса CObject.
В одной строке программы можно использовать оператор << несколько раз. Такая запись сокращает исходный код программы и делает его более легким для понимания, так как все операторы << будут сгруппированы вместе.
Для записи в архивный файл массивов удобнее использовать метод Write класса CArchive:
void Write(const void* lpBuf, UINT nMax);
throw(CFileException);
Он позволяет записать в файл определенное количество байт из указанного буфера памяти.
Параметр lpBuf является указателем на сохраняемую область данных, а параметр nMax определяет количество байт из этой области, которое надо записать в файл.
Если требуется сохранить строку символов, закрытую нулем, то гораздо удобнее вместо метода Write использовать метод WriteString. Метод WriteString записывает в архивный файл строку lpsz:
void WriteString(LPCTSTR lpsz);
throw(CFileException);