Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Разработка Windows - приложений в среде Borland. Учебное пособие
.pdf
первую строчку (work.exe [AppExpert]) и выберите в контекстном
меню пункт Add node.
В вызванном диалоге Add to Project List откройте файл staff.cpp.
В окне проекта появится соответствующий узел. Можно также добавить к узлу проекта staff.cpp узел staff.h, хотя это не обязательно:
41

Откройте модуль workapp.h и вместо строк кода с описанием
структуры staff и массива crew:
struct staff{ // состав сотрудников, штат
char name[20]; // имя
char job[20]; // должность
int pay; // оклад
};
staff crew[100]; // массив записей
int iCur; // номер текущей записи
int iMax; // количество записей в
базе данных
поместите директиву препроцессора #include "staff.h". Если бы среди полей класса не было скрытых (в нашем случае – поле pay), то
больше никаких
проверки этого утверждения достаточно перенести (или добавить)
объявление «public:» перед строчкой:
int pay; // оклад
в модуле staff.h. Более того, будет работать без дополнительных изменений и полное приложение, созданное в предыдущей главе.
Если же оставить поле «pay» скрытым, то необходимо внести
незначительные изменения в созданный проект. В
ции класса диалога (wrkdlgcl.cpp) в коде функции обработчика
кнопки «Добавить» (BNClickedAdd) вместо инструкции:
crew[iMax].pay=atoi(buff);
написать:
crew[iMax].SetPay(atoi(buff));
и в методе ShowRecord (показать запись) заменить
wsprintf(buff,"%i",crew[iCur]. pay );
изменений в приложении можно и не делать. Для
модуле реализа-
42

на
wsprintf(buff,"%i",crew[iCur].GetPay());
Запустите приложение. Обратите внимание, что пользователю
не удастся задать оклад меньше минимального значения 850, определяемого константой:
const int MinPay =850;
заданной в файле wrkdlgcl.cpp.
Более полный проект, реализованный в предыдущей главе, так-
же будет функционировать после внесения аналогичных изменений.
С
НОВА КОМБИНИРОВАННЫЙ СПИСОК
В предыдущей главе комбинированный список, предназначен-
ный для выбора должности, дополняет соответствующее поле нашей анкеты. Рассмотрим теперь возможность использования списка
вместо редактируемого поля. Выберите в окне проекта файл
workapp.rc и запустите редактор ресурсов (двойной щелчок по узлу
в окне проекта или контекстное меню узла → View → Edit
Resources). Раскройте пункт DIALOG
(щелкнув по значку +), выбе-
рите IDD_CLIENT (2000) и дважды щелкните мышкой (или контекстное меню → Edit…). Удалите поле, предназначенное для задания
должности, затем выберите на панели инструментов элемент Combo
Box (комбинированный список) и поместите его в окне диалога на
место удаленного поля. Задайте имя объекта «список» (ClassExpert
→ идентификатор списка → Add instance variable
→ и введите имя
ComboJob). Как и в первой главе найдите через Class Expert виртуальную функцию SetupWindow класса диалога TWorkDlgClient и
перегрузите её – контекстное меню → Add handler. Код функции
аналогичен рассмотренному ранее:
void TWorkDlgClient::SetupWindow()
{
TDialog::SetupWindow();
// INSERT>> Your code here.
43

ComboJob -> AddString("дворник");
ComboJob -> AddString("сторож");
ComboJob -> AddString("сантехник");
ComboJob -> SetSelIndex(0);
}
Далее отредактируйте модуль wrkdlgcl.cpp – в функции-
обработчике BNClickedAdd для кнопки «добавить» замените инструкцию:
EditJob -> GetText(buff,20);
на:
ComboJob -> GetText(buff,20);// копируем текст из поля списка в
массив
и в методе ShowRecord замените инструкцию:
EditJob -> SetText(crew[iCur].job);
на следующие:
int index = ComboJob -> FindString(crew[iCur].job, 0);
if (index<0) MessageBox ("Должность не\nсоответствует-\nштат-
ному расписанию",
"Должность", MB_OK | MB_ICONSTOP);
ComboJob -> SetText(crew[iCur].job);
Здесь предусмотрена проверка должности
ненной в файле, с имеющимся списком должностей. Метод Find-
String позволяет найти индекс строки списка, начинающейся со
строки, являющейся первым параметром метода FindString. В случае
если в списке искомой строки нет, возвращается значение –1 и выдается предупреждение. Подобная проверка желательна, если, например, список имеющихся должностей хранился в отдельном
ле инициализации и был изменен со дня последнего сохранения базы данных.
для записи, сохра-
фай-
44

Запустите программу на исполнение. Интерфейс стал более на-
глядным:
ОНТЕЙНЕРЫ. РЕДАКТИРОВАНИЕ
К
До сих пор для отображения наших данных в памяти мы ис-
пользовали массивы. Это удобно когда максимальное количество
записей известно заранее, в противном случае мы либо искусственно
ограничиваем возможности нашей программы, либо предъявляем
завышенные требования к ресурсам, например, если зарезервируем
массив размерности 1000000. Динамические массивы в данном случае мало эффективны
, так как в процессе работы требуемый размер
базы данных может измениться, что потребует усложнения программы. Кроме того, при удалении записей нам приходится «переписывать» большую часть массива (посмотрите в предыдущей главе
действия, выполняемые при нажатии кнопки «удалить»). Все эти
проблемы легко обойти, если вместо массивов использовать контейнерные классы,
являющиеся частью стандарта языка С++. Контейнеры и алгоритмы входят в состав STL (Standard Template Library –
стандартная библиотека шаблонов) [2-6, 11]. В данной библиотеке
также реализованы полезные алгоритмы, например поиска и сортировки, которые позволяют упростить разработку приложения. Для
наших целей подходят различные контейнеры. Выбор вектора обусловлен только тем, что он допускает индексацию, работа с
ним
аналогична работе с массивом и потребует наименьших изменений в
уже созданных к этому моменту проектах.
45

Итак, приступим. Откройте проект и в заголовочном файле
staff.h замените строку:
extern staff crew[100];
на:
#include <vector.h>
using namespace std;
extern vector <staff> crew;
Затем в модуле реализации класса staff.cpp замените объявление
массива:
staff crew[100];
на контейнер:
vector <staff> crew;
Мы объявили вместо массива пустой вектор. Можно создать
вектор из ста записей – vector <staff> crew(100) и не менять остальной код, но тогда нам по-прежнему придется следить за тем
когда количество записей превысит сотню, программа увеличивала
бы размер вектора.
Поскольку наш вектор будет иметь размер, в точности совпадающий с количеством записей, то при добавлении новой записи
нужно увеличивать вектор. Для этого в файл реализации класса диалога (wrkdlgcl.cpp) добавьте в код функции обработчика кнопки
«Добавить» (BNClickedAdd) после строчки:
char buff[20];
инструкцию:
crew.push_back(staff());
то есть мы сначала добавляем в конец вектора запись со значениями
полей, заданными по умолчанию: оклад - 0, имя – “Unknown”,
46
, чтобы,

должность - "***", а затем заменяем значения полей данными, считанными из соответствующих полей диалогового окна. При этом
индекс последней добавленной записи равен количеству записей до
этого добавления, то есть iMax. Вместо переменной iMax, значение
которой определяет количество записей в базе данных, можно использовать метод контейнера size, возвращающий размер вектора
(то есть iMax = crew.size()). Параметр iMax
мизации вносимых изменений в созданный ранее код. Кроме того,
обращение к методу требует несколько большего времени, чем считывание значения переменной.
Проверьте работоспособность измененного приложения. Вид
диалогового окна остался прежним, так как интерфейс пользователя
не менялся.
Теперь реализуем в нашей программе операции удаления, упорядочивания и поиска
ритмами, предоставляемыми библиотекой STL, что существенно
облегчит нашу работу и значительно уменьшит код.
Добавьте в окно диалога кнопку «Удалить» и создайте функцию-обработчик BNClickedDel. Можно ограничиться инструкциями:
crew.erase(crew.begin()+iCur);
iMax--;
ShowRecord();
Для удаления текущей записи из контейнера применяется метод
erase, параметром которого является итератор (указатель) на удаляемый элемент.
контейнера crew. Таким образом, удаляется элемент вектора с индексом iCur (текущая запись). При этом новой текущей записью
становится запись, следующая после удаленной. Однако, как и ранее, при работе приложения возможны ошибки, обусловленные тем,
что база может быть пустой или удаляемая запись была последней
по порядку
удаления наша база стала пустой, необходимо очистить все поля
диалога. С учетом этих обстоятельств код функции может быть таким:
// INSERT>> Your code here.
if(crew.empty()){.. // ***
(отсутствует следующая запись). Кроме того, если после
записей. Воспользуемся стандартными алго-
Функция begin указывает на начальный элемент
47
оставлен с целью мини-

MessageBox («База пуста», «Удаление», MB_OK |
MB_ICONERROR); return;
}
crew.erase(crew.begin()+iCur); // ***
if(iMax == 1){ // если удаляем послед-
нюю – чистим все поля
iCur=0; iMax=0;
EditName -> SetText("");
ComboJob -> SetText(""); // ***
EditPay -> SetText("");
StaticNum -> SetText("");
return;
}
if(iCur == iMax-1) iCur--; // ***
iMax--; ShowRecord();
Сравните этот фрагмент с программой, разработанной нами в
предыдущей главе, комментарием «***» отмечены изменения, связанные с применением контейнера вместо массива и комбинированного списка для должности вместо редактируемого поля.
Не сложно реализовать и редактирование записей. Рассмотрим
следующую
тель выбрал нужную запись и отредактировал поля диалога, требующие коррекции:
- проверяем, не пуста ли база данных;
- запрашиваем подтверждение на внесение изменений;
- в случае отказа восстанавливаем информацию в окне диалога;
- если подтверждение получено, заменяем значения данными,
Добавьте в окно диалога кнопку «Изменить» и создайте функ-
цию-обработчик BNClickedChange. Введите в созданную с помощью ClassExpert заготовку функции инструкции:
// INSERT>> Your code here.
char buff[20];
if(crew.empty()){
}
последовательность действий после того, как пользова-
считываемыми из соответствующих элементов
MessageBox ("База пуста", "Коррекция", MB_OK |
MB_ICONERROR); return;
48
управления.

wsprintf (buff,"Изменить запись %i?",iCur+1);
if (MessageBox (buff, «Коррекция», MB_YESNO |
MB_ICONQUESTION)==IDNO){
ShowRecord(); return;
}
EditName -> GetText(buff,20);
strcpy(crew[iCur].name,buff);
ComboJob -> GetText(buff,20);
strcpy(crew[iCur].job,buff);
EditPay -> GetText(buff,20);
crew[iCur].SetPay(atoi(buff));
wsprintf(buff, "Запись %i изменена", iCur+1);
MessageBox(buff, "Коррекция", MB_OK |
MB_ICONINFORMATION);
Запустите программу, введите несколько записей и проверьте
работоспособность операций коррекции и удаления.
ЛГОРИТМЫ. СОРТИРОВКА И ПОИСК
А
Для упорядочивания и поиска записей можно применить стан-
дартные алгоритмы, имеющиеся в библиотеке STL. В этом случае
объем кода существенно сократится. Добавьте кнопку «Сортировать», задайте имя функции обработчика события Click для этой
49

кнопки – BNClickedSort и поместите в тело функции следующие
инструкции:
if(iMax == 0){
MessageBox ("База пуста", "Сортировка", MB_OK |
MB_ICONERROR); return;
}
sort(crew.begin(), crew.end());
iCur=0; ShowRecord();
Не стоит забывать, что алгоритм sort упорядочивает записи,
сравнивая их с помощью перегруженной в классе операции "<"
(меньше). В данном случае – по возрастанию оклада. Если операцию
меньше определить, например, так:
bool operator<(const staff& sl, const staff& sr){
return strcmp(sl.name,sr.name)<0;
}
то будет выполнена сортировка по
На практике часто существует потребность упорядочивания за-
писей по различным полям, по желанию пользователя. Эта задача
легко решается, если вспомнить, что в алгоритме сортировки в качестве третьего параметра может быть указано имя функции или
функционального объекта (класса, в котором определена операция
вызова функции [2, 4]). Добавьте (раскомментируйте) в интерфейс
класса staff (
friend bool operator<(const staff& sl, const staff& sr);
объявление двух дружеских функций:
friend bool CompName(const staff& sl, const staff& sr);
friend bool CompJob(const staff& sl, const staff& sr);
а в модуле реализации класса (staff.cpp) раскомментируйте определения этих функций:
файл staff.h) после объявления:
именам.
50
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
