Вывод графики в консольном приложении
Одним из типов приложений (программ) в Windows является консольное приложение (в Microsoft Visual Studio соответствующий шаблон доступен из меню File/New/Project, тип проекта Visual С++/Win32, шаблон «Win32 Console Application», установить флажок Application Settings/Additional options/ Empty project). Консольное приложение работает в текстовом режиме, позволяя осуществлять ввод с клавиатуры и вывод на экран. При этом программист избавляется от необходимости разработки какого-либо оконного интерфейса. Для работы с графикой обычно разрабатывается некоторый оконный интерфейс. Однако и в окно консольного приложения можно выводить различные графические элементы с использованием интерфейса GDI. При этом для получения контекста отображения функции GetDC необходимо передать идентификатор окна консольного приложения. Для определения идентификатора окна консольного приложения можно использовать функцию GetConsoleWindow:
HWND hwnd = GetConsoleWindow ();
Однако если при работе консольного приложения используется консольный ввод-вывод, то его надо будет «разделять» с графикой. Если это не удобно, то может быть целесообразным вывод графики в окно другого приложения, например, в окно стандартного текстового редактора «Блокнот». Для определения идентификатора окна в этом случае можно использовать функцию FindWindowA:
hwnd = FindWindowA (lpClassName, lpWindowName);
Параметры lpClassName и lpWindowName – указатели на строки, содержащие имя оконного класса и заголовок искомого окна, при этом можно задать только один из параметров, указав в качестве другого параметра значение NULL. Функция FindWindowA в случае обнаружения искомого окна возвращает его идентификатор. Для редактора «Блокнот» оконный класс имеет имя «Notepad».
Рассмотренный прием вывода графики в консольном приложении достаточен для обучения, однако является нестандартным. Последнее проявляется в том, что программист не может полноценно управлять окном, в которое графика выводится, не все графические функции выполняются в нем адекватно нашей задаче. Так, при манипуляции окном (при прокрутке, изменении размера, при появлении окна или его части из-за другого окна и т.д.) приложение должно перерисовывать окно (или его часть). Поскольку приложение «не знает», что мы выводим графику в его окно, наши результаты будут стерты. В заключение приведем пример простой программы, выводящей в окно консольного приложения текст и графику с использованием функций GDI.
#include <windows.h> #include <windowsx.h> #include <stdio.h> #include <conio.h> void main () { // получаем идентификатор окна HWND hwnd = GetConsoleWindow (); // получаем контекст отображения HDC hdc = GetDC (hwnd); RECT rt; char buf[100]; // устанавливаем цвет фона SetBkColor (hdc, RGB(0, 0, 0)); // устанавливаем цвет текста SetTextColor (hdc, RGB(255, 0, 0)); // создаем красное перо HPEN hRedPen = CreatePen (PS_SOLID, 5, RGB(255, 0, 0)); // и выбираем его в контекст отображения, // сохраняя предыдущее перо HPEN hOldPen = SelectPen (hdc, hRedPen); // создаем зеленую кисть HBRUSH hGreenBrush = CreateSolidBrush (RGB(0, 255, 0)); // и выбираем ее в контекст отображения, // сохраняя предыдущую кисть HBRUSH hOldBrush = SelectBrush (hdc, hGreenBrush); // выводим строку стандартными средствами printf("Graphics in Console Window."); do{ // получаем размер окна GetClientRect (hwnd, &rt); // формируем выводимую строку sprintf (buf, "Размер окна %d на %d пикселей", rt.right, rt.bottom); // выводим строку графическими средствами TextOutA (hdc, 10, 10, buf, strlen(buf)); // рисуем закрашенный эллипс Ellipse (hdc, 10, 30, rt.right–10, rt.bottom–10); } while (getch ()!=27); // при нажатии любой клавиши // (кроме Esc) перерисовываем изображение, // изображение изменится, если изменились размеры окна, // нажатие Esc – выход // выбираем в контекст отображения предыдущее перо
SelectPen (hdc, hOldPen); // выбираем в контекст отображения предыдущую кисть SelectBrush (hdc, hOldBrush); // удаляем красное перо DeletePen (hRedPen); // удаляем зеленую кисть DeleteBrush (hGreenBrush); // освобождаем контекст отображения ReleaseDC (hwnd, hdc); } Варианты заданий
1. Окружность. 2. Выпуклый четырехугольник. 3. Прямоугольник. 4. Квадрат. 5. Параллелограмм. 6. Трапеция. 7. Треугольник. 8. Ромб.
5. Контрольные вопросы
1. Что такое «контекст отображения», как его получить и освободить? 2. Какую систему координат имеет контекст отображения, как определить размер окна? 3. Что такое «перо», какие имеются встроенные перья, как выбрать перо в контекст отображения? 4. Как создать новое перо, какие его характеристики можно задать, как удалить перо? 5. Что такое «кисть», какие имеются встроенные кисти, как выбрать кисть в контекст отображения? 6. Как создать новую кисть, какие ее характеристики можно задать, как удалить кисть? 7. Как нарисовать точку, как узнать цвет точки? 8. Что такое текущая позиция пера, как ее установить и узнать? 9. Как нарисовать прямую линию? 10. Как нарисовать ломаную линию? 11. Как нарисовать прямоугольник? 12. Как нарисовать эллипс? 13. Как нарисовать многоугольник? 14. Как вывести в окно текст, как установить цвет букв? ЛАБОРАТОРНАЯ РАБОТА №2 КЛАССЫ Цель работы
Изучить средства работы с классами на языке С++.
Содержание работы
1. Ознакомиться со следующими вопросами и понятиями: определение типа данных пользователя с помощью конструкции class, открытая и закрытая части описания класса, определение набора функций класса, интерфейс и реализация класса, реализация понятия модульности в языке С++. 2. Модифицировать программу, разработанную в лабораторной работе №1, так чтобы в ней был определен класс, реализующий понятие геометрической фигуры в графической системе. 2.1. Определить ответственность класса. Учесть, что общение с пользователем (включая ввод данных с клавиатуры и вывод данных на экран, если их предполагается использовать), а также реакцию на возникающие при работе с функциями класса ошибки следует производить вне функций класса. 2.2. Определить атрибуты, необходимые для реализации класса. Поместить атрибуты в закрытую часть описания.
2.3. Определить функции, необходимые для реализации класса. Выделить интерфейс класса и поместить его в открытую часть описания. Включить в разработанный класс функции – устанавливающие и изменяющие геометрические и графические характеристики фигуры (set-функции); – возвращающие геометрические и графические характеристики фигуры (get-функции); – рисующие фигуру на экране; – изменяющие положение фигуры на экране; – обеспечивающие сохраняемость объекта: сохранение набора атрибутов объекта класса в файле и считывание его из файла (файлы для сохранения и считывания должны иметь один формат). 2.4 Обработку ошибок (нулевой радиус окружности, нарушение неравенства треугольника, другие ошибки задания фигуры, ошибки работы с графикой и др.) осуществлять с использованием механизма возбуждения и обработки исключительных ситуаций. 2.5. Разработать функцию, демонстрирующую поведение класса. Поместить реализацию класса в один файл, а демонстрационную функцию – в другой. Обеспечить необходимые межмодульные связи. 3. Подготовить текстовые файлы с разработанной программой, оттранслировать их, собрать и выполнить программу с учетом требований операционных систем и программных оболочек, в которых эта программа выполняется. При необходимости исправить ошибки и вновь повторить технологический процесс решения задачи. 4. Оформить отчет по лабораторной работе. Отчет должен содержать постановку задачи, описание разработанных классов (атрибутов и функций), алгоритм, текст разработанной программы и результаты тестирования. 5. Защитить лабораторную работу, ответив на вопросы преподавателя. Методические указания С точки зрения языка программирования класс объектов можно рассматривать как тип данных, а отдельные объекты – как данные этого типа. Описание класса подобно описанию структуры, однако для класса используется ключевое слово class вместо struct. В описание класса включаются данные (атрибуты) и функции, изменяющие их (функции-члены класса).
Описание класса разделяется на закрытую и открытую части, помеченные как private и public.
struct Point; //Точка class Circle // класс окружность { private: // закрытая часть, содержит атрибуты и // служебные функции Point Сenter; // центр окружности int Radius; // радиус окружности ... public: // открытая часть, содержит интерфейс void draw(); // нарисовать окружность ... };
Открытая часть образует открытый интерфейс объектов класса. Это функции, используемые для взаимодействия с другими объектами и функциями. Атрибуты и функции в закрытой части могут использоваться только функциями-членами класса (а также друзьями класса). С атрибутами объектов класса работают только посредством соответствующих функций. В частности, для получения значений атрибутов объекта и изменения их используются get- и set-функции соответственно.
Point get_Сenter (); // возвращает значение центра окружности int get_Radius (); // возвращает значение радиуса окружности void set_Сenter (Point new_center); // изменяет центр окружности void set_Radius (int new_radius); // изменяет радиус окружности
Объекты нового типа вводятся так же, как и переменные стандартных типов:
Circle C;
Функции-члены можно вызывать только для переменной соответствующего типа. Например, получить значение радиуса окружности можно так:
int r = C.get_Radius ();
Для обеспечения сохраняемости объектов (набора их атрибутов) между сеансами работы программы их возможно сохранить в файле с последующим чтением из него. Для этого необходимо описать соответствующие функции.
void load (char* file_name); void save (char* file_name);
Описание класса обычно не содержит реализации (тела) функций-членов. Исключение составляют инлайн-функции. Реализация класса помещается отдельно от описания. Реализация класса и пользовательский код (код, в котором будут использоваться объекты класса), помещаются в раздельно компилируемых частях программы. Как правило, описание класса помещается в так называемый заголовочный файл, имеющий расширение h или hpp. Заголовочный файл обычно включается и в файл с пользовательским кодом, и в файл с реализацией класса. Это достаточно простой и эффективный способ обеспечить идентичность представления интерфейса в обоих файлах. Файл с описанием класса Circle.h:
class Circle { ... // только описания };
Файл с пользовательским кодом User.cpp:
#include "Circle.h" // включить интерфейс int main(void) { ... Circle C; ... C.set_Radius(r); ... }
Файл, содержащий реализацию класса Circle.cpp:
#include "Circle.h" // включить интерфейс Point Circle:: get_Сenter () { return Сenter; }; int Circle:: get_Radius () { return Radius; }; void Circle:: set_Сenter (Point new_center) { Сenter = new_center };
void Circle:: set_Radius (int new_radius) { Radius = new_ radius };
В случае нарушения какого-либо условия (возникновения ошибки) следует сгенерировать исключительную ситуацию (исключение). Фрагмент кода, в котором может возникнуть исключение, помещается в пробный блок – блок try. Если при выполнении операторов, находящихся внутри блока try, происходит исключительная ситуация, то управление передается обработчикам исключений, которые задаются ключевым словом catch и находятся ниже блока try.
try{ // пробный блок ... } catch(int error){...}
Исключение генерируется посредством указания ключевого слова throw с необязательным аргументом-выражением.
throw 1;
Исключение будет обработано посредством вызова того обработчика catch, тип параметра которого будет соответствовать типу аргумента throw. При наличии вложенных блоков try (например, из-за вложенности вызовов функций) будет использован обработчик самого глубокого блока. Если обработчика, соответствующего типу аргумента throw, на данном уровне не будет найдено, будет осуществлен выход из текущей функции и поиск в блоке try с меньшей глубиной вложенности и т.д. После обработки исключения управление передается на оператор, следующий за описаниями обработчиков catch. Рассмотрим пример изменения значения радиуса окружности. Изменим реализацию set-функции:
void Circle:: set_Radius (int new_radius) { if (new_ radius<=0) throw 0; Radius = new_ radius };
Фрагмент пользовательского кода будет следующим:
try{ C.set_Radius(rad);...} catch(int error){if (error == 0) printf("Ошибка задания радиуса.");...} 4. Контрольные вопросы
1. Принцип абстрагирования: а. В чем заключается принцип абстрагирования? б. Что такое барьер абстракции, уровни абстракции? в. В чем заключается принцип наименьшего удивления? г. Что такое контрактная модель программирования? д. Что такое сигнатура операции? е. Что такое инвариант?
2. Исключения: а. Как определить пробный блок и обработчики исключений? б. Как осуществить возбуждение исключительной ситуации? в. Как происходит обработка исключений?
3. Инкапсуляция: а. В чем заключается принцип инкапсуляции? б. В чем смысл разделения класса на интерфейс и реализацию? в. Что такое класс? Как он определяется? Как определяются операции для класса? г. Как определяется открытая и закрытая части тела класса? д. Что такое друг класса?
4. Модульность: а. Что такое модуль? б. В чем заключается принцип модульности? в. Модульность на языке С++? г. Какие существуют приемы эффективного разделения программы на модули?
5. Иерархичность: а. В чем заключается принцип иерархичности? б. Что такое иерархия «является»? в. Что такое иерархия «имеет»?
6. Типизация: а. В чем заключается принцип типизации? б. Что такое полиморфизм? в. Что такое принудительное приведение? г. Что такое перегрузка? д. Какие операторы приведения вы знаете?
7. В чем заключается принцип параллелизма? 8. В чем заключается принцип сохраняемости?
ЛАБОРАТОРНАЯ РАБОТА №3 ОБЪЕКТЫ Цель работы
Изучить понятие объекта, средства его создания и уничтожения. Содержание работы
1. Ознакомиться с вопросами использования конструкторов и деструкторов для создания и уничтожения объектов. 2. По предложенному преподавателем варианту разработать программу на языке С++, в которой был бы определен класс-контейнер, т.е. класс объектов, служащих для хранения объектов класса, разработанного в предыдущей лабораторной работе. Среди различных типов контейнеров упомянем списки, стеки, очереди, деревья, таблицы. Контейнер должен быть реализован как динамическая структура данных. Разработать следующие функции-члены класса: конструктор, деструктор, функции для помещения объектов-фигур в контейнер, их возвращения (удаления), поиска в контейнере, «распечатки содержимого» контейнера – вывода информации о содержащихся в объекте-контейнере объектах и/или их графических образов, а также функции, обеспечивающие сохраняемость контейнера с использованием файла. Реализацию класса-контейнера поместить в отдельный файл. Разработать функцию, демонстрирующую поведение объекта-контейнера с несколькими объектами-фигурами. 3. Подготовить текстовые файлы с разработанной программой, оттранслировать их, собрать и выполнить программу с учетом требований операционных систем и программных оболочек, в которых эта программа выполняется. При необходимости исправить ошибки и вновь повторить технологический процесс решения задачи. 4. Оформить отчет по лабораторной работе. Отчет должен содержать постановку задачи, описание разработанных классов, алгоритм, текст разработанной программы и результаты тестирования. 5. Защитить лабораторную работу, ответив на вопросы преподавателя.
Методические указания Важными видами операций являются конструктор и деструктор. Конструктор – это операция создания объекта и/или его инициализации; в С++ конструктор имеет то же имя, что и класс. Деструктор – это операция, освобождающая ресурсы, которые использует объект, и/или разрушающая сам объект; в С++ имя деструктора состоит из имени класса, перед которым ставится знак «тильда» – «~». Данные операции обеспечивают инфраструктуру, необходимую для создания и уничтожения экземпляров класса. Если у класса есть конструктор, то он вызывается всегда, когда создается объект класса. Если у класса есть деструктор, то он вызывается всегда, когда объект класса уничтожается. Если программист не описал в классе конструктор и деструктор, то они будут созданы автоматически.
class Open_Stack { // класс-контейнер: «открытый» стек int *s, length, top; public: Open_Stack(int n); // конструктор, n – размер стека ~ Open_Stack(); // деструктор void push(const int el); // помещение элемента в контейнер int pop(); // возвращение элемента с удалением из контейнера bool find(const int el); // поиск элемента в контейнере, возвращаемое // значение – найден или нет ... };
При использовании динамических структур данных конструктор может использоваться для (первоначального) выделения памяти, а деструктор – для ее освобождения. Рассмотрим конструктор и деструктор класса Open_Stack, реализуемого на основе массива, память под который выделяется и освобождается динамически.
Open_Stack(int n): length(n), top(0) // инициализаторы конструктора {s = new int [length];} // выделение памяти ~Open_Stack() { delete [ ] s; } // освобождение памяти В реализации последнего конструктора для инициализации отдельных частей объекта использованы инициализаторы конструктора. Важность инициализаторов в том, что только с их помощью можно инициализировать константные члены, члены, являющиеся ссылками, а также члены, являющиеся объектами класса, в котором есть один или несколько конструкторов, но отсутствует конструктор по умолчанию (конструктор без параметров). Важно также, что такая инициализация выполняется эффективнее, поскольку создание объекта в C++ начинается с инициализации его атрибутов конструктором по умолчанию, после чего выполняется вызываемый конструктор. Использование инициализаторов позволяет сразу же вызвать нужный конструктор. При поиске в контейнере по ключу, не совпадающему с самим элементом, возвращаемым значением может быть указатель на найденный элемент (но не на элемент структуры данных, используемой для реализации контейнера). Сравним сигнатуры функций поиска в следующем классе.
struct Node{ // звено списка int key; // ключ Circle* pC; // указатель на элемент Node* next; // указатель на следующее звено }; class List{ // список Node* pbeg; // указатель на начало списка ... public: bool find1 (int key); // возможный, но малоинформативный вариант Node* find2 (int key); // неправильный вариант, // нарушается инкапсуляция Circle* find3 (int key); // предпочтительный вариант, можно // изменять объект в контейнере const Circle* find4 (int key); // предпочтительный вариант, нельзя // изменять объект в контейнере ... }; Варианты заданий
1. Пусть каждому объекту из класса, реализующего геометрическую фигуру, сопоставлен некоторый числовой идентификатор. Разработать класс, реализующий понятие таблицы, в качестве ключа использовать указанный идентификатор. Функция поиска ищет элемент по его идентификатору. Таблицу реализовать на основе: а) двунаправленного списка (обеспечить «распечатку» содержимого как в прямом, так и в обратном направлениях); б) хеширования с цепочками (см. рис. 8); в) дерева двоичного поиска.
Рисунок 8*.
2. Пусть все объекты класса, реализующего геометрическую фигуру, разделены на несколько категорий по значению некоторого признака (ввести в рассмотрение некоторый «нетривиальный» признак, либо считать отнесение фигуры к конкретной категории прерогативой пользователя). Разработать класс, реализующий понятие таблицы с дубликатами (повторяющимися ключами), в качестве ключа использовать номер категории. Функция поиска ищет элементы определенной категории. Таблицу реализовать на основе: а) двунаправленного списка (обеспечить «распечатку» содержимого как в прямом, так и в обратном направлениях); б) хеширования с цепочками (см. рис. 8), хеш-значением является номер категории; в) дерева двоичного поиска. 3. Разработать класс, реализующий понятие множества** (с неповторяющимися элементами). Функция поиска ищет фигуры с заданными геометрическими и/или графическими характеристиками. Множество реализовать на основе: а) двунаправленного списка (обеспечить «распечатку» содержимого как в прямом, так и в обратном направлениях); б) хеширования с цепочками (см. рис. 8), хеш-функцию построить на основе ключа поиска. 4. Разработать класс, реализующий понятие множества (см. сноску **) с дубликатами (повторяющимися элементами). Функция поиска ищет фигуры с заданными геометрическими и/или графическими характеристиками. Множество реализовать на основе: а) двунаправленного списка (обеспечить «распечатку» содержимого как в прямом, так и в обратном направлениях); б) хеширования с цепочками (см. рис. 8), хеш-функцию построить на основе ключа поиска. 5. На основе списка разработать класс, реализующий понятие а) «открытого» стека; б) «открытой» очереди; в) «открытого» дека. «Открытость» контейнера подразумевает возможность просматривать элементы в контейнере, осуществлять поиск. Функция поиска ищет фигуры с заданными геометрическими и/или графическими характеристиками. 6. Разработать класс, реализующий понятие таблицы (см. задание к варианту 1): а) неупорядоченной таблицы; б) упорядоченной таблицы; в) хеш-таблицы. При реализации использовать массив, память под который выделяется и освобождается динамически, максимальный размер таблицы задается как параметр конструктора. Объекты класса «фигура» хранятся в контейнере по ссылке (указателю). 7. Разработать класс, реализующий понятие таблицы с дубликатами (см. задание к варианту 2): а) неупорядоченной таблицы; б) упорядоченной таблицы; в) хеш-таблицы. При реализации использовать массив, память под который выделяется и освобождается динамически, максимальный размер таблицы задается как параметр конструктора. Объекты класса «фигура» хранятся в контейнере по ссылке (указателю). 8. Разработать класс, реализующий понятие а) «открытого» стека; б) «открытой» очереди; в) «открытого» дека. Контейнер реализуется на основе массива, память под который выделяется и освобождается динамически, размер массива (максимальная вместимость контейнера) задается как параметр конструктора. «Открытость» контейнера подразумевает возможность просматривать элементы в контейнере, осуществлять поиск (функция поиска ищет фигуры с заданными геометрическими и/или графическими характеристиками). Объекты класса «фигура» хранятся в контейнере по ссылке (указателю).
5. Контрольные вопросы
1. Что такое состояние объекта, чем оно определяется? 2. Что такое поведение объекта, посредством чего оно реализуется? 3. Какие виды типичных операций над объектами вы можете назвать? 4. Каким образом могут создаваться объекты? 5. Для чего используется ключевое слово explicit? 6. Что такое инициализатор конструктора, в каких случаях его использование обязательно? 7. Что такое свободная подпрограмма? 8. Что такое идентичность объекта? 9. Что такое структурная зависимость, какие проблемы она порождает? 10. Что такое конструктор копирования, какие проблемы могут возникнуть при использовании конструктор копирования, предоставляемого по умолчанию? 11. Какие проблемы могут возникнуть при использовании оператора присваивания, предоставляемого по умолчанию? 12. Что такое связь? 13. Что такое агрегация (отношение между объектами)? ЛАБОРАТОРНАЯ РАБОТА №4
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|