Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ООП_Лабораторный практикум

.pdf
Скачиваний:
79
Добавлен:
11.05.2015
Размер:
1.5 Mб
Скачать

////////Мой код заканчивается здесь///////////

return TRUE; // return TRUE unless you set the focus to a control

}

Функция SetTimer() имеет 3 параметра: первый - идентификатор таймера; второй - устанавливает период в миллисекундах, с которым будет происходить сообщение WM_TIMER; третий - задает адрес функции, которая будет выполняться каждые 500 миллисекунд. Поскольку вы передаете в третий параметр значение NULL, это означает что вы не определяете функцию, следовательно каждые 500 миллисекунд будет генерироваться сообщение

WM_TIMER.

Затем выполняется оператор IF, который проверяет, установился ли таймер, если функция SetTimer() вернула значение 0,то таймер не установлен, и выполняется функция MessageBox, которая сообщает с помощью предопределенной панели со значком "Error", что таймер не инсталлирован.

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

После того, как таймер установлен, вы можете удалить его используя функцию KillTimer(). Параметром этой функции является номер удаляемого таймера. В нашей программе вы должны тоже удалить таймер при выходе из программы. Для этого необходимо связать функцию KillTimer() с событием WM_DESTROY.

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

1.Выберите ClassWizard в меню View

2.Выберите закладку Message Maps в панели ClassWizard

3.Используйте диалоговую панель ClassWizard для выбора следующего события: Class Name: CGraphDlg

Object ID: СGraphDlg Messages: WM_DESTROY

4.Нажмите кнопку Edit Code и напишите следующий код в функции OnDestroy(): void CGraphDlg::OnDestroy()

{

CDialog::OnDestroy();

// TODO: Add your message handler code here

////Здесь начинается мой код////

KillTimer(1);

////Здесь заканчивается мой код////

}

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

WM_TIMER.

1.Выберите ClassWizard в меню View

2.Выберите закладку Message Maps в панели ClassWizard

3.Используйте диалоговую панель ClassWizard для выбора следующего события: Class Name: CGraphDlg

Object ID: СGraphDlg Messages: WM_TIMER

4.Нажмите кнопку Edit Code и напишите следующий код в функции OnTimer(): BOOL CGraphDlg::OnTimer(UINT nIDEvent)

{

////////Мой код начинается здесь///////////

MessageBeep((WORD)-2);

////////Мой код заканчивается здесь///////////

51

CDialog::OnTimer(UINT nIDEvent);

}

Сохраните и запустите программу. Вы должны слышать, что через каждые 500 миллисекунд компьютер будет подавать звуковой сигнал. Завершите программу Graph, перед тем как продолжить работу надо поместить функцию MessageBeep() в комментарий.

5.Событие WM_PAINT.

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

Чтобы показать необходимость события WM_PAINT, напишем следующий код.

1.Выберите ClassWizard в меню View

2.Выберите закладку Message Maps в панели ClassWizard

3.Используйте диалоговую панель ClassWizard для выбора следующего события: Class Name: CGraphDlg

Object ID: IDC_DRAWGRAPHICS_BUTTON Messages: BN_CLICKED

4.Щелкните на кнопку Add Fucntion и в раскрывшемся окне нажмите кнопку OK.

5.Нажмите кнопку Edit Code и напишите следующий код в функции

OnDrawgraphicsButton():

BOOL CGraphDlg::OnDrawgraphicsButton()

{

// TODO: Add your message handler code here

////////Мой код начинается здесь///////////

///Создать объект контекста устройства(DC) CClientDC dc(this);

//Создать новое перо

CPen MyNewPen;

MyNewPen.CreatePen(PS_SOLID, 10, RGB(255,0,0)); //Выбрать перо

CPen* pOriginalPen; pOriginalPen=dc.SelectObject(&MyNewPen); CRect MyRectangle(20, 10, 120,110); //Нарисовать круг dc.Ellipse(&MyRectangle);

//Выбрать первоначальное перо dc.SelectObject(pOriginalPen);

////////Мой код заканчивается здесь///////////

CDialog::OnPaint();

}

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

Код перерисовки.

Итак, окно в функции OnPaint() надо перерисовывать, добавим следующий код:

1.Выберите ClassWizard в меню View

2.Выберите закладку Message Maps в панели ClassWizard

52

3.Используйте диалоговую панель ClassWizard для выбора следующего события: Class Name: CGraphDlg

Object ID: CGraphDlg Messages: WM_PAINT

4.Нажмите кнопку Edit Code и напишите следующий код в функции OnPaint(): void CGraphDlg::OnPaint()

{

if (IsIconic())

{

}

else

{

////Мой код начинается здесь////

OnDrawgraphicsButton();

////Мой код заканчивается здесь////

//...

}

}

Вы добавили функцию, которая будет каждый раз выполняться, когда окно будет необходимо перерисовать. Вспомните, что функция OnDrawgraphicsButton() рисует круг – соответственно изображение будет перерисовываться.

Модификация функции OnPaint().

В предыдущем разделе вы перерисовывали экран с помощью функции OnDrawgraphicsButton(), в будущем мы напишем другой код в этой функции, поэтому ее выполнение не желательно. Значит, нам необходимо код перерисовки экрана связать непосредственно с событием WM_PAINT.

Модифицируйте код в функции OnPaint() следующим образом: void CGraphDlg::OnPaint()

{

if (IsIconic())

{

//....

}

else

{

////Мой код начинается здесь////

// OnDrawgraphicsButton();

///Создать объект контекста устройства(DC) CPaintDC dc(this);

//Создать новое перо

CPen MyNewPen;

MyNewPen.CreatePen(PS_SOLID, 10, RGB(255,0,0)); //Выбрать перо

CPen* pOriginalPen; pOriginalPen=dc.SelectObject(&MyNewPen); CRect MyRectangle(20, 10, 120,110); //Нарисовать круг dc.Ellipse(&MyRectangle);

//Выбрать первоначальное перо dc.SelectObject(pOriginalPen);

////Мой код заканчивается здесь////

53

//...

}

}

Постройте и запустите программу Graph. Подвигайте окном Graph и убедитесь, что код перерисовки функции OnPaint() работает.

6.Визуальное проектирование диалоговой панели IDD_CUSTOM_DIALOG и связывание событий с элементами управления(продолжение).

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

Проектирование диалоговой панели

Сейчас мы визуально спроектируем диалоговую панель. Для этого вначале выберите закладку "Resourse View" и раскройте пункт Graph Resource. Выполните следующие действия

Расставьте элементы управления как показано на рис. 6.1 и согласно таблице 6.2.

 

 

Таблица 6.2 Оконные элементы программы.

Объект

Свойство

Установка

 

Dialog Box

ID

IDD_CUSTOM_DIALOG

 

 

Caption

Set Graph

 

 

Font

System, Size 10, страница Styles

 

Radio Button

ID

IDC_RED_RADIO

 

 

Caption

&Red

 

 

Group

отмечен, страница General

 

 

Modal frame

отмечен, страница Extended Styles

 

 

Client edge

отмечен, страница Extended Styles

 

 

Static edge

отмечен, страница Extended Styles

 

Radio Button

ID

IDC_GREEN_RADIO

 

 

Caption

&Green

 

 

Group

не отмечен, страница General

 

 

Modal frame

отмечен, страница Extended Styles

 

 

Client edge

отмечен, страница Extended Styles

 

 

Static edge

отмечен, страница Extended Styles

 

Radio Button

ID

IDC_BLUE_RADIO

 

 

Caption

&Blue

 

 

Group

не отмечен, страница General

 

 

Modal frame

отмечен, страница Extended Styles

 

 

Client edge

отмечен, страница Extended Styles

 

 

Static edge

отмечен, страница Extended Styles

 

Связывание переменной с радиокнопками.

Теперь вам необходимо связать переменную с радиокнопками, которые вы разместили в диалоговой панели IDD_CUSTOM_DIALOG. Выполните правый щелчок по пункту IDD_CUSTOM_DIALOG, и выберите пункт ClassWizard в меню View. Появиться диалоговая панель(Add class), которая спросит вас: создать новый класс или добавить в существующий. Вы выбираете создать новый Create a new class, и щелкаете по кнопке OK. В диалоговой панели New Class задайте имя CSetDlg. И щелкните по кнопке OK. Убедитесь, что окно в Class Name

54

установлено на CSetDlg. Затем щелкните на пункт Member Varibles и выбирите пункт IDC_RED_RADIO. Нажмите на кнопку Add Varible и задайте в появившемся диалоговом окне

Variable Name: m_RedRadio Category: Value Variable Type: int . Щелкните по кнопке OK.

Следовательно, когда значение перменной m_RedRadio равно 0 - выбрана радиокнопка RED, когда 1 - радиокнопка CREEN, когда 2 - радиокнопка BLUE.

Создание объекта класса CSetDlg

Сейчас вы создадите объект класса CSetDlg. В объявление класса CGraphDlg(в файле CGraphDlg.h) добавьте следующий код, который объявляет переменную m_dlg объектом класса CSetDlg. Это нужно для того, чтобы мы могли иметь доступ к переменным класса CSetDlg:

class CGraphDlg : public CDialog

{

// Construction public:

CGraphDlg(CWnd* pParent = NULL); // standard constructor

////Мой код начинается здесь////

CSetDlg m_dlg;

////Мой код заканчивается здесь////

...

}

Введенный вами код создает объект m_dlg класса CSetDlg. Однако, когда компилятор встречает слово CSetDlg в файле CGraphDlg, он его не поймет. Значит надо добавить следующий оператор #include в файл CGraphDlg, в самое начало:

// GraphDlg.h : header file

//

////Мой код начинается здесь////

#include "SetDlg.h"

////Мой код заканчивается здесь////

...

...

Модификация кода кнопки Draw Graphics

1.Выберите ClassWizard в меню View

2.Выберите закладку Message Maps в панели ClassWizard

3.Используйте диалоговую панель ClassWizard для выбора следующего события: Class Name: CGraphDlg

Object ID: IDC_DRAWGRAPHICS_BUTTON Messages: BN_CLICKED

4.Щелкните на кнопку Add Fucntion и в раскрывшемся окне нажмите кнопку OK. Нажмите кнопку Edit Code и напишите следующий код в функции OnDrawgraphicsButton():

BOOL CGraphDlg::OnDrawgraphicsButton()

{

// TODO: Add your message handler code here

////////Мой код начинается здесь///////////

m_dlg.DoModal();

////////Мой код заканчивается здесь///////////

}

Вы добавили единственнный оперaтор: m_dlg.DoModal(); который вызывает диалоговую панель IDD_CUSTOM_DIALOG(выводит объект m_dlg).

Инициализация радиокнопок.

55

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

1.Выберите ClassWizard в меню View

2.Выберите закладку Message Maps в панели ClassWizard

3.Используйте диалоговую панель ClassWizard для выбора следующего события: Class Name: CGraphDlg

Object ID: CGraphDlg Messages: WM_INITDIALOG

4.Щелкните на кнопку Add Fucntion и в раскрывшемся окне нажмите кнопку OK. Нажмите кнопку Edit Code и напишите следующий код в функции OnInitDialog():

BOOL CGraphDlg::OnInitDialog()

{

CDialog::OnInitDialog(); //....

////////Мой код начинается здесь///////////

///Установить системный таймер int iInstallResult; iInstallResult=SetTimer(1, 50, NULL); if(iInstallResult==FALSE)

{

MessageBox("Cannot install timer", "Error message", MB_OK+MB_ICONERROR);

}

//Выбрать радиокнопку RED m_dlg.m_RedRadio=0;

////////Мой код заканчивается здесь///////////

return TRUE; // return TRUE unless you set the focus to a control

}

Добавление переменных элементов в класс CGraphDlg

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

class CGraphDlg : public CDialog

{

// Construction public:

CGraphDlg(CWnd* pParent = NULL); // standard constructor

////Мой код начинается здесь////

CSetDlg m_dlg; int m_Radius; int m_Direction;

////Мой код заканчивается здесь////

...

}

Инициализация радиокнопок

Вам нужно написать код, который инициализирует переменные m_Radius и m_Direction. Используйте диалоговую панель ClassWizard для выбора следующего события:

Class Name: CGraphDlg

56

Object ID: CGraphDlg

Messages: WM_INITDIALOG

Напишите следующий код в функции OnInitDialog(): BOOL CGraphDlg::OnInitDialog()

{

CDialog::OnInitDialog(); //....

////////Мой код начинается здесь///////////

///Установить системный таймер int iInstallResult; iInstallResult=SetTimer(1, 50, NULL); if(iInstallResult==FALSE)

{

MessageBox("Cannot install timer", "Error message", MB_OK+MB_ICONERROR);

}

//Выбрать радиокнопку RED m_dlg.m_RedRadio=0; m_Radius=50; m_Direction=1;

////////Мой код заканчивается здесь///////////

return TRUE; // return TRUE unless you set the focus to a control

}

Связывание кода с событием Timer.

Используйте диалоговую панель ClassWizard для выбора следующего события: Class Name: CGraphDlg

Object ID: СGraphDlg

Messages: WM_TIMER

Напишите следующий код в функции OnTimer(): BOOL CGraphDlg::OnTimer(UINT nIDEvent)

{

//....

////////Мой код начинается здесь///////////

//MessageBeep((WORD)-2); m_Radius=m_Radius+m_Direction; if(m_Radius>=100)

{

m_Direction=-1;

}

if(m_Radius<=10)

{

m_Direction=1;

}

//Инициировать исполнение функции OnPaint() Invalidate();

////////Мой код заканчивается здесь///////////

CDialog::OnTimer(UINT nIDEvent);

}

57

Модификация кода, связанного с событием WM_PAINT.

Используйте диалоговую панель ClassWizard для выбора следующего события: Class Name: CGraphDlg

Object ID: CGraphDlg

Messages: WM_PAINT

Модифицируйте код в функции OnPaint() следующим образом: void CGraphDlg::OnPaint()

{

if (IsIconic())

{

//....

}

else

{

////Мой код начинается здесь////

// OnDrawgraphicsButton();

///Создать объект контекста устройства(DC) CPaintDC dc(this);

//Создать новое перо

CPen MyNewPen;

MyNewPen.CreatePen(PS_SOLID, 10, RGB(255,0,0)); //Выбрать перо

CPen* pOriginalPen; pOriginalPen=dc.SelectObject(&MyNewPen); // CRect MyRectangle(20, 10, 120,110);

CRect MyRectangle(20, 10, 20+m_Radius*2,10+m_Radius*2); //Нарисовать круг

dc.Ellipse(&MyRectangle); //Выбрать первоначальное перо dc.SelectObject(pOriginalPen);

////Мой код заканчивается здесь////

//...

}

}

Упражнение 1. Модифицируйте функцию OnPaint() таким образом, чтобы цвет круга можно было выбирать с помощью диалоговой панели IDD_CUSTOM_DIALOG.

Упражнение 2. Добавьте такой код в программу, чтобы, после выбора цвета в диалоговой панели IDD_CUSTOM_DIALOG, он менялся до того, как вы нажмете на кнопку OK.

Часть 2. Наследование и механизм виртуальных функций

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

Определение производного класса имеет следующий синтаксис: class ID_производного_класса : ID_базового_класса{

код_производного_класса

};

58

Одна из особенностей порожденного класса - видимость унаследованных компонент базового класса со степенью защиты protected и public (атрибуты базового класса). Компоненты базового класса с ключевым словом private производному классу недоступны.

Производный класс может служить базовым классом для создания следующего, производного класса на более низком уровне иерархии классов. Наследование называется простым, если производный класс имеет одного родителя. В C++ наследование может быть множественным. Множественное наследование позволяет одному классу обладать свойствами двух и более родительских классов.

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

class ID_производного_класса : public ID_базового_класса

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

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

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

Продемонстрируем данный механизм на конкретном примере:

 

class В 1 {

// Базовый класс

protected:

 

int x;

 

public:

 

B1 (int i) { x=i;}

// Конструктор с параметрами

};

 

class D: public В1 {

// Наследование с сохранением прав доступа

protected:

 

int a, b;

 

public:

D(int i, int j) : B1(i+3), a(i) {

// Конструктор со списком инициализации

b=j;

void Print(void) {

cout « " a = " « a « " b = " « b « " x = " « x « endl; } };

void main(void) { D dl(3,4); dl.Print(); }

59

На экране монитора: а = 3 b = 4 x = 6

Как правило, коды методов выносят из пространства класса. Тогда для предыдущего примера:

class D { protected: int a, b;

public: D(int, int); void Print(void); };

D::D(int I, int j) : B1(i+3), a(i) { b=j;} Oioa

void D::Print(void) {

cout« " a = " « a « " b = " « b « " x = " « x « endl;}

Использование косвенной адресации с установкой указателей на базовый класс

Механизм косвенной адресации рассмотрим на конкретном примере.

class В { public: int x;

B() b{ x=4;} // Конструктор по умолчанию

};

class D : public В { // Производный класс

public:

 

int y;

 

D() { y=5;}

// Конструктор по умолчанию

};

 

void main(void) {

// Конструктор класса D создает объект d

D d;

В *р;

// Указатель установлен на базовый касс

р = &d;

// Указатель р инициализируется адресом d

//Косвенное обращение к объектам базового и производного классов

//Считываем их текущее состояние в переменные

int i = р -> х;

// Базовый класс виден напрямую

int j = ((D* ) р )р -> у;

// Прямое преобразование указателя на D

// Через переменные печатаем их текущее состояние cout« " x_i = " «i « " у_j = " «j « endl;

getch();

};

На экране монитора: x_i = 4 y_j = 5

Механизм виртуальных функций

Механизм виртуальных функций - одна из основных концепций объектноориентированного программирования. Данный механизм предполагает использование идеи «один интерфейс — множество методов реализации». Эта идея заключается в том, что базовый класс обеспечивает для производных классов все элементы, которые эти классы могут использовать непосредственно, а также содержит набор функций, которые производные классы должны реализовать путем их переопределения (создание собственного кода функции в производном классе, которые позволяют решить поставленную перед производным классом задачу).

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

60