- •Овсянник в.Н. Лабораторные работы по курсу «Объектно-ориентированное программирование»
- •13. Разработка приложения с архитектурой «документ-вид». Часть 2 120
- •14. Разработка приложения с архитектурой «документ-вид». Часть 3 135
- •1.Разработка типового консольного приложения
- •1.1.Методические указания
- •1. Запуск и настройка ис mvs-2008.
- •2.Поиск данных в массиве
- •3.Обработка текстового файла
- •4.Задача «куча камней»
- •5.Реализация алгоритма полного перебора
- •6. Исследование погрешностей вычислений
- •7.Поиск экстремумов с ограничениями или «Брачное агенство»
- •Постановка задачи
- •Задание
- •Листинг файла mAgency.Cpp (с главной функцией)
- •Листинг файла Lib.H
- •Листинг файла Lib.Cpp
- •8.Приложение с окном вида
- •9.Разработка графического редактора
- •Задание.
- •9.1.Проектирование программы
- •9.2.Разработка интерфейса
- •Установка флагов
- •Создание меню и панели инструментов
- •Обработка сообщений о нажатии кнопки мыши
- •9.3.Рисование
- •9.4.Растягивание фигур
- •9.5.Обновление изображения
- •9.6.Сохранение метафайла на диске и его загрузка
- •10.Приложение, основанное на модальном диалоговом окне
- •Методические указания Модальные и немодальные диалоговые окна
- •Ресурсы и элементы управления
- •Сценарий создания приложения
- •Разбор приложения
- •Усовершенствование приложения
- •11.Приложение с контекстным меню и строкой состояния
- •11.1.Методические указания к первой части работы
- •11.2.Сценарий выполнения первой части работы
- •11.3.Методические указания ко второй части работы Строка состояния
- •Определение секций в строке состояния
- •Строка сообщений
- •Индикатор состояния
- •Управление строкой состояния
- •11.4.Сценарий выполнения второй части работы
- •12.Разработка приложения с архитектурой «документ-вид». Часть 1
- •12.1.Сценарий создания приложения
- •13.Разработка приложения с архитектурой «документ-вид». Часть 2
- •13.1.Методические указания Класс коллекций cObList
- •13.2.Сценарий выполнения работы
- •14.Разработка приложения с архитектурой «документ-вид». Часть 3
- •14.1.Методические указания
- •15.Класс вектор
- •15.1.Пример класса tVector
- •15.2.Класс tVector с перегруженными операциями
- •16.Приложение с таблицей
- •16.1.Вариант 1 Сценарий выполнения работы
- •16.2.Вариант 2 Сценарий выполнения работы
13.Разработка приложения с архитектурой «документ-вид». Часть 2
Программисты действуют рационально, лишь тогда, когда другие способы исчерпаны
Закон Мерфи
Цель работы – освоить использование списка объектов в приложении, основанном на архитектуре «документ-вид» (4 час.).
Приложение, которое должно быть разработано в этой второй части работы, отличается от приложения первой части следующими свойствами:
Вместо одного объекта класса CStudent документ должен содержать список таких объектов. (Теперь Вы понимаете, почему мы использовали класс CStudent вместо того, чтобы сделать m_strName и m_nGrade переменными-членами класса «документ».)
Кнопки на панели инструментов приложения должны позволять перемещаться по списку объектов CStudent.
Структура программы теперь должна допускать дополнительные представления данных. Команда Clear All теперь передается объекту «документ» и поэтому в игру вступают функции UpdateAllViews и OnUpdate для обновления его представления.
Сценарий этой работы, как и предыдущей, основан на материале глав 16 и 17 работы [1].
Задание.
Проработайте методические указания по работе со списками объектов.
Создайте приложение в соответствии с приведенным ниже сценарием.
Выполните отладку и добейтесь работоспособности приложения. Не забудьте о самостоятельной работе по доработке приложения (шаг 11).
Осмыслите полученный результат, примененный инструментарий и приемы программирования операций со списком объектов.
Сдайте работу.
13.1.Методические указания Класс коллекций cObList
Познакомившись с классами коллекций, Вы удивитесь, как до сих пор без них обходились. Один из представителей этого семейства классов — CObList. Освоив его, Вы легко разберетесь в других классах списков (list classes), а также классах массивов (array classes) и классах ассоциативных массивов (map classes).
Может быть, Вам показалось, что коллекции – это что-то новое? Однако язык программирования С всегда поддерживал одну из их разновидностей – массивы. Однако, размер массивов в языке С фиксирован, они не поддерживают вставку новых элементов или удаление имеющихся, не контролируют выход индекса за границы массивов. Многие программисты на С писали библиотеки для других коллекций, включая связанные списки (linked lists), динамические массивы (dynamic array) и индексируемые словари (indexed dictionaries). В языке C++ есть очевидная и более удачная (по сравнению с библиотекой С-функций) альтернатива по реализации коллекций – это классы. Например, объект-список корректно инкапсулирует внутренние структуры данных списка.
Класс CObList поддерживает упорядоченные списки указателей на объекты классов, производных от CObject. Другой MFC-класс коллекций, CPtrList, вместо указателей на CObject хранит указатели void. Почему бы не использовать его вместо CObList? Класс CObList имеет ряд преимуществ при выводе диагностической информации, в чем Вы убедитесь далее, и при сериализации (serialization). Одно из важных свойств CObList — способность хранить смешанные указатели (mixed pointers). Другими словами, коллекция CObList может одновременно содержать указатели как на объекты CStudent, так и на объекты, например, CTeacher, если оба эти класса производны от CObject.
Применение класса CObList для создания списков типа FIFO
Одно из простейших примеров применений объекта CObList — добавление новых элементов в конец и выборка элементов из начала списка. Элемент, первым внесенный в список, будет первым и извлечен из него (принцип FIFO — First In, First Out, т. е. «первым вошел, первым вышел»). Допустим, элементы списка — объекты созданного Вами класса CAction, производного от CObject. Приведенная ниже консольная программа заносит в список пять элементов, а затем извлекает их в том же порядке.
#include <afx.h>
#include <afxcoll.h>
class CAction : public CObject
{
private:
int m_nTime;
public:
CAction(int nTime) { m_nTime = nTime; } /* Конструктор запоминает время как целое число */
void PrintTime() { TRACE("time = %d\n", m_nTime); }
};
int main()
{
CAction* pAction;
CObList actionList; // список создается в стеке
int i;
// вставляем в список actionList объекты CAction в порядке от 0 до 4
for (i = 0; i < 5; i++)
{
pAction = new CAction(i);
actionList.AddTail(pAction); // приведение типа для pAction не требуется
}
// извлекаем и удаляем CAction в порядке от 0 до 4
while (!actionList.IsEmpty())
{
pAction =(CAction*) actionList.RemoveHead();/* Для возвращаемого
значения приведение типа необходимо */
pAction->PrintTime();
delete pAction;
}
return 0;
}
Вот что происходит в этой программе. Сначала создается объект actionList класса CObList. Затем при помощи функции-члена CObList::AddTail() в конец списка добавляются указатели на вновь создаваемые объекты pAction. Преобразование типа для pAction не требуется, так как параметром служит указатель на CObject, a pAction – указатель на производный от него класс (вспомните правила совместимости классов одной иерархии). Далее указатели на объекты CAction извлекаются из начала списка с помощью функции-члена CObList::RemoveHead() и объекты уничтожаются. В данном случае для возвращаемого функцией RemoveHead() значения необходимо приведение типа, поскольку она возвращает указатель на класс, расположенный в иерархии классов выше класса CAction.
Когда Вы извлекаете указатель на объект из коллекции, сам объект автоматически не уничтожается. Удалять объекты CAction нужно с помощью операции delete, что и выполняется в программе.
Перебор элементов CObList – переменная типа POSITION
Предположим, Вам надо «пройти» все элементы списка. Класс CObList имеет функцию-член GetNext(), которая возвращает указатель на «следующий» элемент списка, но принцип ее использования необычен. GetNext() принимает целочисленный параметр типа POSITION *. Он содержит внутреннее представление положения извлекаемого элемента в списке. Так как параметр POSITION передается по ссылке (&), функция может изменить его значение.
Функция GetNext() выполняет следующие операции.
1. Возвращает указатель на «текущий» элемент списка, который определяется передаваемым ей значением POSITION.
2. Изменяет значение параметра POSITION так, чтобы он соответствовал положению следующего элемента списка.
Вот как выглядит цикл с GetNext(), если Вы используете список из предыдущего примера:
CAction* pAction;
POSITION pos = actionList.GetHeadPosition();
while (pos != NULL) {
pAction = (CAction*) actionList.GetNext(pos);
pAction->PrintTime();
}
Теперь допустим, что у Вас есть приложение, в котором для перемещения вперед и назад по элементам списка применяются кнопки панели инструментов. Для выбора очередного элемента функция GetNext() не годится, потому что она всегда увеличивает значение переменной POSITION, а предугадать, следующий или предыдущий элемент потребуется пользователю, просто невозможно. Взгляните на пример обработчика командного сообщения в классе «вид», который выбирает следующий элемент списка. Здесь m_actionList – объект типа CObList, внедренный в класс CMyView, а m_position – переменная типа POSITION, хранящая положение текущего элемента списка.
CMyView::OnCommandNext()
{
POSITION pos;
CAction* pAction;
if ((pos = m_position) != NULL)
{
m_actionList.GetNext(pos);
if (pos != NULL) // pos==NULL в конце списка
{
pAction = (CAction*) m_actionList.GetAt(pos);
pAction->PrintTime();
m_position = pos;
}
else AfxMessageBox("Достигнут конец списка");
}
}
Чтобы увеличить переменную положения – вызывают GetNext(), а чтобы получить элемент – используют функцию CObList::GetAt(). Переменная m_position обновляется, только если конец списка еще не достигнут.
Класс-шаблон коллекции CTypedPtrList
Класс CObList отлично работает, когда нужна коллекция, содержащая смешанные указатели. Однако если требуется набор с безопасным приведением типов, содержащий указатели только на один тип объектов, используйте классы-шаблоны коллекций указателей из MFC-библиотеки. Пример такого шаблонного класса — CTypedPtrList. Шаблоны (templates) – относительно новый элемент C++, впервые появившийся в Microsoft Visual C++ 2.0. CTypedPtrList — это класс-шаблон, который можно использовать для создания списка указателей на объекты любого заданного класса. Не вдаваясь в подробности, скажем только, что шаблон применяют для создания нового класса списка, производного либо от CPtrList, либо от CObList.
Объявить объект-коллекцию для указателей на CAction можно так:
CTypedPtrList <CObList, CAction *> m_actionList;
Первый параметр шаблона – это базовый класс для набора, второй — тип параметров и возвращаемых значений. В качестве базовых допускаются только классы CPtrList и CObList, так как других классов коллекций указателей в библиотеке MFC нет. Если Вы храните объекты классов, производных от CObject, используйте как базовый класс CObList; в ином случае — CPtrList.
При таком использовании шаблона, как показано выше, компилятор гарантирует, что все функции-члены списка возвращают указатель на CAction. Таким образом, можно писать:
pAction = m_actionList.GetAt(pos); // приведение типа не требуется
Чтобы слегка упростить описание объекта m_actionList, сгенерируем некое подобие самостоятельного класса с помощью описания типа typedef:
typedef CTypedPtrList <CObList, CAction *> CActionList;
и тогда m_actionList можно объявить так:
CActionList m_actionList;
Диагностика и классы наборов
Функция Dump для CObList и других классов наборов обладает одним весьма полезным свойством. Вызвав Dump для какого-либо объекта-коллекции, Вы получите информацию о каждом объекте из коллекции. Если в классах объектов, составляющих коллекцию, применены макросы DECLARE_DYNAMIC и IMPLEMENT_DYNAMIC, в информации появится имя класса каждого объекта.
По умолчанию функции Dump для коллекций выводят только имена классов и адреса объектов в коллекции. Чтобы функции Dump для коллекций вызывали функцию Dump для каждого элемента коллекции, где-то в начале программы надо сделать вызов:
#ifdef _DEBUG
afxDump.SetDepth(1);
#endif
Тогда оператор
#ifdef _DEBUG
afxDump << actionList;
#endif
будет выводить информацию примерно так:
a CObList at $411832
with 4 elements
a CAction at $412CD6
time = 0
a CAction at $412632
time = 1
a CAction at $41268E
time = 2
a CAction at $4126EA
time = 3
Если в наборе содержатся смешанные указатели, для класса объекта вызывается виртуальная функция Dump и выводится имя соответствующего класса.
