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

Учебное пособие 1407

.pdf
Скачиваний:
6
Добавлен:
30.04.2022
Размер:
1.12 Mб
Скачать

1. Подготовка рабочей области проекта и вставка класса ССаr

В среде Visual C++ создаётся пустое консольное приложение Win32 с именем CarInCPP. Вставляется новый пустой файл с именем main.cpp в рабочее пространство проекта. Для использования потоков ввода/вывода C++ включается <iostream.h> перед определением головного блока main:

#include <iostream.h> void main(void)

{

}

Вставьте новый класс в программу: щелкните на пиктограмме проекта правой кнопкой мыши на вкладке ClassView и выберите команду New Class... из меню.

В появившемся диалоговом окне New Class введите имя нового класса в поле Name и нажмите кнопку ОК.

Этот диалог автоматически определяет конструктор без параметров и виртуальный деструктор для класса, а также снабжает определение класса вызовами препроцессора во избежание ошибок, связанных с переопределениями. Зададим постоянные значения для верхнего предела скорости и максимальной длины имени. Это можно сделать прямо в заголовочном файле CCаr (вне описания класса) с использованием ключевого слова C++ const вместо директивы препроцессора

#define языка С:

const int MAX_LENGTH =100; const int MAX_SP

EED = 500;

2. Определение открытого интерфейса ССаr

Позволим классу ССаr поддерживать следующий открытый интерфейс:

// Car.h

const int MAX_LENGTH = 100; const int MAX_SPEED = 500;

19

#include <iostream.h>

 

#include <stdio.h>

// Для функции gets()

#include <string.h>

// Для строк

class ССаr

 

{

 

public:

// Открытый интерфейс класса

ССаr();

virtual ~ССаr();

void DisplayCarStats() ; void SpeedUp();

// Функции доступа обычно определяются как встраиваемые (inline) int GetCurrSpeed() { return m_currSpeed;}

int GetMaxSpeed() { return m_maxSpeed;} char* GetPetName() { return m_petName;}

// Обобщенная функция типа Set void CreateACar();

private:

// Закрытые данные

 

char m_petName[MAX_LENGTH];

 

int m_maxSpeed;

 

int m_currSpeed;

};

Глобальные функции помещены в открытую часть класса. Обратите внимание на заголовки методов: нет необходимости передавать какие-либо параметры в DisplayCarStats() или Speedup(), поскольку они работают непосредственно с закрытыми данными объекта.

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

20

3. Реализация методов класса ССаr

Конструктор, задаваемый по умолчанию, присваивает параметрам состояния объекта пустые значения с помощью спи-

ска инициализации: // Конструктор ССаr

ССаr::ССаr() : m_currSpeed(0), m_maxSpeed(0)

{

//Вместо присвоения этим параметрам

//нулевых значений внутри блока

//используется список инициализации strcpy(m_petName, "");

}

Метод CreateACar() запрашивает имя машины и максимальную скорость, используя gets(), cout и cin:

// Создание новой машины void CCar::CreateACar()

{

char buffer [MAX_LENGTH]; int spd =0;

cout<<"Введите имя вашей машины:"<< flush;

gets(buffer); // Если желаете, проверьте соответствие с МАХ strcpy(m_petName, buffer);

do // Скорость в заданных пределах?...

{

cout << "Введите максимальную скорость вашей машины: " << flush; cm » spd;

cout << endl;

}while(spd > MAX_SPEED); m_maxSpeed = spd;

}

Поскольку эта функция оперирует с внутренними закрытыми параметрами состояния, DisplayCarStats() не требует аргументов.

// Реализация DisplayCarStats.

21

void CCar::DisplayCarStats()

{

cout << "***********************************" << endl; cout << "Имя машины: " << m_petName << endl;

cout << "Ее максимальная скорость: " << m_maxSpeed << endl; cout << "***********************************" << endl << endl;

}

SpeedUp() // увеличивает скорость и печатает результат: void CCar::Speedup()

{

if(m_currSpeed <= m_maxSpeed)

{

m_currSpeed = m_currSpeed + 10;

cout << "Скорость: " << m_currSpeed << endl;

}

}

Определены класс и реализация его методов. Переходите к компиляции.

22

4. Реализация головного блока

Теперь все, что надо делать, — это посылать сообщения в экземпляр класса CCаr:

#include "car.h" #include <iostream.h> void main(void)

{

cout << "***********************************" << endl; cout << " Программа Car на С++"<< endl;

cout << "***********************************"<< endl;

CCar myCar;

// Конструктор без параметров

myCar.CreateACar();

// Запрос на ввод данных

myCar.DisplayCarStats();

// Показ состояния

while(myCar.GetCurrSpeed() <= myCar.GetMaxSpeed())

myCar.SpeedUp();

// Разгоняемся! Взрыв!

cout << myCar.GetPetNameO << " взлетела на воздух! Ходи пешком!" << endl;

}

Контрольные вопросы

1.Что представляет собой модель автомобиля, которая рассматривалась в домашнем задании?

2.Каков порядок разработки и выполнения программ в среде Visual C++?

23

Домашнее задание №2 для подготовки к занятиям по разработке сетевого приложения для Windows на С++

Цель задания Подготовиться к занятиям по разработке сетевого при-

ложения для Windows на языке С++. Изучить применение методов интерфейса IUnknown.

Для освоения работы с GUID, HRESULT, счетчиком ссылок, строками СОМ и логику IUnknown разработаем программу CoCarApp.exe. Она пока не является стандартным СОМ клиент/серверным приложением: объект СОМ создается и исполняется внутри одиночного исполняемого файла.

Кокласс CоСаr, построенный здесь, будет использоваться на следующем занятии, где мы поместим класс СОМ в исполняемый "домик" DLL, который затем будет доступен для СОМ-клиента.

Пример проекта данной работы содержится в каталоге

… \Ch3\CoCarApp3-1.

1. Проектирование интерфейса

Для проекта консольного приложения Win32, выберите Empty application. Разработаем класс, поддерживающий три пользовательских интерфейса. В предыдущем домашнем заданиия класс CCаr предоставлял все функциональные возможности автомобиля через единственный открытый интерфейс. Этот интерфейс можно разбить на логические группы (интерфейсы). Создадим новый заголовочный файл с именем interfaces.h и определим три новых интерфейса, которые моделируют поведение автомобиля:

/* IStats используется для получения имени автомобиля,а также для отображения параметров состояния реализуемого объекта */ DECLARE_INTERFACE_(IStats,IUnknown)

24

{

STDMETHOD(DisplayStats)() PURE; STDMETHOD(GetPetName)(BSTR* petName) PURE;

};

// IEngine моделирует поведение двигателя. //Можем ускорять его, получать значения //максимальной и текущей скорости

DECLARE_INTERFACE_(IEngine, IUnknown)

{

STDMETHOD(SpeedUp)() PURE; STDMETHOD(GetMaxSpeed)(int* maxSpeed) PURE; STDMETHOD(GetCurSpeed)(int* curSpeed) PURE;

};

/* ICreateCar используется для присвоения имени реализуемому объекту и задания максимальной скорости */

DECLARE_INTERFACE_(ICreateCar, IUnknown)

{

STDMETHOD(SetPetName)(BSTR petName) PURE; STDMETHOD(SetMaxSpeed)(int maxSp) PURE;

};

Все пользовательские интерфейсы СОМ должны быть произведены от IUnknown, так что не забудьте добавить

#include <windows.h> в файл. interfaces.h. Также добавьте к определению интерфейса директивы препроцессора

#ifndef /#define/ #endif, чтобы предотвратить ошиб-

ки повторного определения.

Присвойте GUID каждому пользовательскому интерфейсу. Создайте другой заголовочный файл с именем iid.h, который будет содержать описания макросов define_guid. Используя guidgen.exe, создайте три новых GUID с соответствующими константами (с префиксом IID_). Вот примеры GUID для каждого интерфейса (у вас будут, конечно же, другие):

// {A533DA30-D372-11d2-B8CF-0020781238D4}

25

DEFINE_GUID(IID_IEngine,

Oxa533da30, Oxd372, Ox11d2, Oxb8, Oxcf, 0x0, 0x20, 0x78, 0x12, 0x38, Oxd4);

//{A533DA31-D372-11d2-B8CF-0020781238D4} DEFINE_GUID(IID_IStats,

. . .

//{A533fiA32-D372-11d2-B8CF-0020781238D4} DEFINE_GUID(IID_ICreateCar,

. . .

Создайте еще один новый файл с именем iid.cpp. Он должен содержать только следующие ниже #include. He забудьте вставить его в рабочее пространство проекта (исполь-

зуйте меню Project | Add To Project | Files).

#include <windows.h>

// Для всего, относящегося к СОМ

#include <initguid.h>

// Для DEFINE_GUID

#include "iid.h"

// IID

Иметь один централизованный файл для всех GUID ваших приложений очень полезно, если вам потребуется включать определения в другие проекты. Полезно иметь и соответствующий файл *.срр для ID ваших интерфейсов.

2. Реализация IUnknown

Вставьте новый класс C++ с именем СоCаr, содержащий интерфейсы IEngine, IStats и ICreateCar. Понадобит-

ся реализовать не только методы каждого пользовательского интерфейса, но и три метода IUnknown. Вот начальный заголовочный файл для СоCаr:

// СоСаr реализует четыре интерфейса СОМ

#include "interfaces.h"

class CoCar : public IEngine, public ICreateCar, public IStats

{

public:

CoCar();

virtual ~CoCar(); // IUnknown

26

STDMETHODIMP QueryInterface(REFIID riid, void** pIFace); STDMETHODIMP_(ULONG)AddRef(); STDMETHODIMP_(ULONG)Release();

// IEngine

STDMETHODIMP SpeedUp();

STDMETHODIMP GetMaxSpeed(int* maxSpeed); STDMETHODIMP GetCurSpeed(int* curSpeed); // IStats

STDMETHODIMP DisplayStats(); STDMETHODIMP GetPetName(BSTR* petName); // ICreateCar

STDMETHODIMP SetPetName(BSTR petName); STDMETHODIMP SetMaxSpeed(int maxSp) ;

};

Мы не будем использовать cout в реализации методов интерфейсов Car. На этот раз возьмем функцию MessageBox() Win32 API. Это позволит нам перенести класс СоСаr в исполняемую DLL в следующей работе, не привязываясь к консольному клиенту Win32.

Сначала реализуем счетчик ссылок. Добавьте закрытый (private) счетчик ссылок с именем m_refcount (типа ULONG) к классу СоСаr и обнулите его в конструкторе. Функция AddRef() будет добавлять к счетчику единицы, a Release() — отнимать единицы и проверять на окончательный сброс, после которого объект сам себя выгрузит из памяти с помощью команды delete this.

// Схема счетчика ссылок объекта

STDMETHODIMP_(ULONG) CoCar::AddRef()

{

return ++m_refCount;

}

STDMETHODIMP_(ULONG) CoCar::Release()

{

if (--m_refCount == 0)

27

{

delete this; return 0;

}

else

return m_refCount;

}

Теперь реализуем Queryinterface(), чтобы возвращать указатели интерфейса для IUnknown, IStats, ICreateCar и IEngine. He забудьте вызвать AddRef(),

если вы предоставляете указатель интерфейса, и вернуть e_nointerface в случае отсутствия поддержки интерфейса:

//QueryInterfасе() отвечает за видимость

//указателя "this" для данного клиента

STDMETHODIMP CoCar::QueryInterface(REFIID riid, void** pIFace)

{

// Что конкретно от меня хотят? if(riid == IID_IUnknown)

{

*pIFace = (IUnknown*) (IEngine*)this;

}

else if (riid == IID_IEngine)

{

*pIFace = (IEngine*)this;

}

else if (riid = IID_IStats)

{

*pIFace = (IStats*) this;

}

else if (rud == IID_ICreateCar)

{

*pIFace = (ICreateCar*)this;

}

else

28