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

Штерн В. - Основы C++. Методы программной инженерии - 2003

.pdf
Скачиваний:
278
Добавлен:
13.08.2013
Размер:
28.32 Mб
Скачать

620 Часть III • Программирование с агрегированием и наследованием

считываются в память, информация о категории сохраняется в цифровой форме (1 — художественный фильм, 2 — комедия, 3 — фильм ужасов). При повторном сохранении данных в файле категория фильма снова сохраняется как буква.

Когда клиент приносит в магазин фильм для регистрации, продавец вводит номер телефона клиента для поиска в базе данных. Если клиента не находят, отображается сообщение. Если клиент есть в базе данных, отображаются имя и номер телефона клиента, а также данные о фильмах, которые он уже взял на­ прокат. После проверки имени клиента продавец вводит идентификатор фильма, количество имеющихся в наличии картин уменьшается на единицу, а идентифика­ тор фильма добавляется в список идентификаторов фильмов, взятых этим клиен­ том напрокат.

Если для ввода идентификатора фильма используется считыватель штрих-кода, то фильм обязательно будет найден в базе данных. При вводе идентификатора фильма вручную, если фильм не найден, отображается сообщение об ошибке.

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

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

Классы и их ассоциации

Список классов приложения часто составляется при анализе функциональных спецификаций или другого документа, который описывает интерфейс и поведение пользователя системы.

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

Одно из предостережений в отношении использования описания системы для построения модели заключается в том, что описание построено на сущностях, взаимодействующих с системой (например, клиент, продавец магазина, база данных). Цель моделирования состоит в создании реализации системы, которая включает классы, содержащие данные, и оперирует этими данными (например, Customer, StoreClerk и Database). Объекты из описания системы и классы из реализации системы могут иметь одинаковые имена, но они не идентичны.

Предположим, что модель классов должна включать следующие классы: Item (информация о фильме). Customer (информация о клиенте). Inventory (управление набором фильмов и совокупностью клиентов). File (управление базой данных фильмов и клиентов) и Store (управление интерфейсом пользователя и запрос сервисов других классов). На рис. 14.7 показаны эти классы с указанием их атри­ бутов и операций.

Какие классы здесь связаны друг с другом? Следует признать, что изложенная система описания не очень помогает разгадать это. С другой стороны, не стоит торопиться признавать свою ошибку. Описание обычно создается, чтобы помочь в формировании и тестировании программы, а не для того, чтобы облегчить рисо­ вание моделей UML для системы.

 

Глава 14 • Выбор между наследованием и композицией

621

Item

Customer

Inventory

File

Store

1 title

name, phone

ItemList

f:fstream

 

id, quant

count, movies

' itemCount

getltemO

loadDataO

1 category

 

itemldx

set()

saveltemO

findCustomerO

 

custLlst

set()

addMovieO

getCustomerO

processltemO

custCount

getQuantO

removeMovieO

saveCustomerO

saveDataO

custldx

getldO

getCustomer()

trimO

 

 

 

getltemO

 

append ltem()

 

 

printltemO

 

appendCustO

 

 

IncrQtyO

 

getltemO

 

 

 

 

getCustomerO

 

 

 

 

printRentalO

 

 

 

 

checkOutO

 

 

 

 

checkInO

 

 

Рис. 14.7.

Нотация UML для классов

с атрибутами

и операциями

 

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

Однако в большинстве книг по объектно-ориентированному анализу и проек­ тированию приводятся примеры диаграмм классов, используется описание систе­ мы как исходной точки без последуюш,ей реализации и, что более важно, без оценки того, как модель влияет на решения. Между тем решения о том, как распределить атрибуты и операции между классами и как связать классы, влияют на сложность программ.

Классы Item и Customer выполняют роль серверов для других классов. Они представляют такие услуги, как сохранение и извлечение значений элементов данных.

Впроектах с несколькими файлами спецификация каждого класса помещается

вотдельный заголовочный файл. Заголовочные файлы включаются в исходные файлы, которые реализуют клиентов класса, т. е. в исходные файлы, использую- ш.ие имя класса для определения его переменных или параметров. В листинге 14.6 представлен заголовочный файл для класса Item, с его элементами данных и функ­ циями-членами. Класс обеспечивает элементы данных для заголовка фильма, идентификатора, количества, имеюш,егося в наличии, и категории. Методы позво­ ляют клиентской программе установить значения элементов данных объекта Item

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

Листинг 14.6. Спецификация класса для класса Item (файл item. h).

/ / f i l e item.h

#ifndef ITEM_H

#define ITEM_H

class Item

{protected:

char title[26];

int id, quant, category;

public:

void set (const char *s, int num, int qty, int type); int getQuantO const;

int getldO const;

622

Часть II! * Программирование с агрегирование^^ и наследованием

void getltem(char* name, int &num, int& qty, int &type) const; void printltemO const;

void incrQty(int qty);

} ; #endif

Обратите внимание на использование директив условной компиляции. В соот­ ветствии с правилами, унаследованными из языка С, заголовочный файл можно включать в исходные файлы своей программы только один раз. Если заголовочный файл включается более чем в один исходный файл, определение типа класса будет компилироваться в каждом исходном классе. Поскольку каждый исходный файл может компилироваться отдельно и в отдельный объектный файл, программа об­ рабатывается несколько раз, а компоновщик отбрасывает дополнительные опре­ деления, имеющие одинаковую структуру. Именно поэтому каждый программист на C++ должен помещать эти директивы условной компиляции в каждый заго­ ловочный файл. Как жаль!

В листинге 14.7 представлен заголовочный файл для класса Customer. Здесь показан тот же набор директив условной компиляции для препроцессора, что

ив листинге 14.6. Класс Customer предоставляет элементы данных для хранения имени клиента, номера телефона, счетчика фильмов, выданных клиенту напрокат,

иидентификатора ка>едого взятого фильма. Его функции-члены позволяют клиент­ ской программе установить значения для имени клиента и номера телефона, добавить идентификатор фильма в список фильмов, удалить идентификатор филь­ ма из списка фильмов и извлечь имя клиента, номер телефона и список фильмов, взятых клиентом напрокат.

Листинг 14.7. Спецификация класса для класса Customer (файл customer, h).

/ / f i l e customer.h

#ifndef CUSTOMER_H

#define CUSTOMER_H

class Customer

{ char name[20], phone[15]; int count;

int movies[10]; public :

Customer ();

void set(const char *nm, const char *ph); void addMovie(int id);

int removeMovie(int id);

void getCustomer(char *nm, char *ph, int &cnt, int m[]) const;

};

#endif

Подобно заголовочным файлам, исходная программа на С4-+ для каждого класса в многофайловом проекте реализуется в отдельном исходном файле. В лис­ тинге 14.8 представлена реализация класса для класса Item. Заголовочный файл <item.h> должен быть включен в этот файл, чтобы убедиться, что компилятору известно значение оператора области действия Item: :.

Реализация указывает, что класс Item для поддержки своей программы не тре­ бует каких-либо других классов. Ему нужны библиотечные средства. Некоторые проектировщики в свои диаграммы UML включают библиотечные компоненты как серверы классов. Вас же интересуют связи между компонентами вашей про­ граммы, а не то, как программа использует библиотечные классы и функции.

Глава 14 • Bbi6op между наследованием и композицией

Листинг 14.8. Реализация класса Item (файл item.cpp)

//file item.cpp

#inclucie <iostream>

 

 

 

using namespace std;

 

// это необходимость

#inGlucle "item.h"

 

void Item: : set(const char *s, int num, int qty, int type)

 

{ strcpy(title,s); id=num; quant=qty; category=type; }

 

int Item::getQuant() const

// используется

Inventory::checkOut()

{

return quant; }

 

 

 

int Item::getld() const

 

// в printRentalO, checkOutO, checkInO

{

return id; }

 

void Item::getltem(char* name, int &num, int& qty,

File::saveltem()

 

int &type) const

// используется

{ strcpy(name,title); num = id;

 

 

 

qty = quant; type = category; }

 

 

void Item: :printltem() const

// используется

printRentalO

{

cout.setf(ios::left, ios::adjustfieid) ;

// ему известны его форматы вывода на печать

 

cout.width(5) ; cout «

id;

 

cout.width(27) ; cout « title;

// другие подтипы компонента

 

switch

(category) {

" feature"; break;

 

case 1: cout «

 

 

 

case 2: cout «

"comedy"; break;

 

 

 

case 3: cout «

" horror"; break; }

 

 

 

cout «

endl; }

 

 

 

 

void Item::incrQty(int qty)

// используется в checkOutO, checkln()

 

{ quant += qty; }

 

 

 

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

ментариях указывается, что класс Item является сервером классов Inventory

и File.

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

Customer. Заголовочный файл "customer, h" включается в этот файл. Заголовок

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

Листинг 14.9. Реализация класса Customer (файл customer, срр)

// file customer.срр

 

#include <iostream>

 

using namespace std;

// это необходимость

#include "customer.h"

Customer::Customer()

{ count = 0; }

void Customer::set(const char *nm, const char *ph)

{ strcpy(name, nm); strcpy(phone, ph); }

// в appendCustO

void Customer::addMovie(int id)

// в appendCustO, в checkOutO

{ movies[count++] = id; }

624

Часть III • Программирование с агрегированием и наследованием

i nt

Customer::removeMovie(int id)

/ / используется в checkInO

{int idx;

for (idx=0; idx < count;

idx++)

/ /

найти фильм

 

 

i f

(movies[idx]

== id)

break;

 

 

 

i f

(idx == count)

return

0;

/ /

возвратится,

если не найден

while

(idx < count

-

1)

 

 

 

 

{

movies[idx] = movies[idx+1];

/ /

сдвиг остатка

влево

 

idx++;

}

 

 

 

 

 

 

count-;

 

 

 

 

/ /

уменьшить счетчик фильмов

return 1;

}

 

 

 

/ /

сообщить об успешном выполнении

void Customer:;getCustomer(char *nm, char *ph,

/ /

saveDataO

 

int

&cnt,

int m[])

const

 

/ /

Inventory::getCustomer()

{ strcpy (nm, name);

strcpy(ph,phone); cnt = count;

 

 

 

for (int i=0; i < count;

i ++)

 

 

 

m[i] = movies [ i ] ;

}

 

 

 

 

Видно, что файл исходной программы "customer, срр" не включает какие-либо заголовочные файлы. Это означает, что класс Customer не содержит серверные классы — он сам обслуживает другие классы. Строковые комментарии в каждой функции указывают, когда функция используется как сервер для обеспечения клиентской программы с доступом к клиентским данным и сервисам.

Конструктор Customer инициализирует счетчик взятых напрокат фильмов, уста­ навливая его в нуль. Метод set() назначает новые значения для имени клиента и номера телефона, а метод addMovieO добавляет новый идентификационный номер в конец списка фильмов, предоставляемых напрокат.

Метод removeMovie() проверяет, находится ли идентификатор фильма в списке фильмов клиента. Если идентификатор в списке отсутствует, функция возвращает нуль, чтобы сообщить о сбое. Если идентификатор в списке имеется, метод сдви­ гает оставшиеся идентификационные номера на одну позицию влево, уменьшает счетчик допустимых идентификаторов фильмов и возвращает 1, чтобы подтвер­ дить успешное выполнение.

Обратите внимание на то, что уменьшается счетчик идентификаторов, а не ко­ личество значений в массиве. Именно поэтому говорится о "счетчике действитель­ ных идентификаторов фильмов", а не о "счетчике идентификаторов фильмов".

Алгоритмы сдвига часто содержат ошибки, которые трудно обнаружить. Этот алгоритм легче понять, если уменьшить счетчик идентификаторов фильмов до выполнения сдвига влево, а не после него.

int Customer::removeMovie

(int id)

// используется в checkInO

{ int

idx;

 

 

// найти фильм

for

(idx=0;

idx < count;

idx++)

i f (movies [idx] == id) break;

 

i f

(idx == count) return

0;

// уменьшить счетчик фильмов

count-;

 

 

while (idx

< count)

 

// обычный вид

{

movies[idx] = movies[idx+1];

// сдвиг остатка влево

idx++; }

}

 

 

return 1 ;

 

 

Многие программисты пишут цикл сдвига в более краткой форме, используя

оператор инкремента в операторе сдвига, а не помещая его в отдельной строке.

while (idx < count)

// кратко, ноопасно

movies[idx] = movies[idx++];

Глава 14 • Выбор между HOCAeAOBaHiiervi и ког^позицией

| 625 |

Вспомните, что присваивание в языке C + + является выражением, а в выра­ жениях C + + гарантирует порядок оценки операций. Это правильно: порядок оценки компонентов в выражении не определен жестко (не гарантирован). Если выражение оценивается слева направо, то приведенный выше цикл работает пре­ красно. Если выражение оценивается справа налево, то в цикле присутствует ошибка. Рекомендуем создавать более подробный код, который легче понять, чем краткий код, который запутывает программиста, осуш,ествляюш,его сопро­ вождение.

Как и в файле "itenr. срр" в листинге 14.7, использованы комментарии к стро­ кам, чтобы указать клиентов метода Customer. Эти комментарии показывают, что класс Inventory использует класс Customer в качестве сервера.

В листинге 14.10 представлен заголовочный файл для класса Inventory. Его элементы данных включают список фильмов и одного из клиентов, счетчики дей­ ствительных компонентов в каждом списке и индексы для организации доступа к компонентам в каждом списке. Его функции-члены позволяют клиентской программе добавить фильм в список компонентов и клиента в список клиентов, извлечь текундий компонент из списка (указанный индексом itemldx), извлечь текущий клиент из списка (указанного индексом custldx), вывести на печать информацию, описываюшую фильмы, взятые напрокат клиентом, отметить вы­ дачу фильмов и зарегистрировать возврат фильма.

Листинг 14.10. Спецификации класса для класса Inventory (файл inventory, h)

/ / f i l e inventory.h

# ifndef INVENTORY_H #define INVENTORY_H #include "item.h"

#inGlude "customer.h"

class Inventory { protected:

enum { MAXM = 5, MAXC = 4 } ; / / только для прототипа Item itemList[MAXM];

Customer custList[MAXC]; int itemCount, custCount; int itemldx, custldx;

public:

Inventory 0 ;

void appendltem (const char* ttl, int id, int qty, intcat); void appendCust (const char* nm, const char* ph,

int cnt, const int *m); int getltem(ltem& item);

int getCustomer(char* nm, char* ph, int &cnt, int *m); void printRentaI(int id);

int checkOut(int id); void checkln(int id);

} ;

#endif

Поскольку класс Inventory является клиентом классов Item и Customer, заго­ ловочный файл Inventory должен включать заголовочные классы Item и Customer.

Некоторые программисты чувствуют себя незащищенными. Поэтому "на вся­ кий случай" включают все заголовочные файлы проекта в каждый файл реализа­ ции. Как они говорят, лучше быть в безопасности, чем потом сожалеть.

I

626

I

Часть III ^ Програ1^1У1ированив с огрегированиелл ы наследованием

 

 

 

Это неправильно. Вредно включать больше, чем нужно. Если что-то не orwi.v.-

 

 

 

чено, то компилятор пометит строки, использующие неопределенные имена, как

 

 

 

содержащие ошибки. Когда включено больше, чем необходимо, исключается риск

 

 

 

появления сообщения об ошибке. Однако для программиста клиентской части

 

 

 

восприятие программы затрудняется.

 

 

 

 

Компилятор игнорирует избыточные определения в type. Читатели также не

 

 

 

обратят на них внимание, но только после проверки программы класса и при обна­

 

 

 

ружении, что эти имена в type не используются в классе.

 

 

 

В данном случае можно поддержать такие рискованные действия (особенно

 

 

 

потому, что при исключении ненужных заголовочных файлов риск отсутствует).

 

 

 

Включение дополнительных заголовочных файлов "на всякий случай" не очень

 

 

 

хорошая практика. Вместо того чтобы избежать синтаксических ошибок, можно

 

 

 

еще больше запутать читателей.

 

 

 

 

Реализация класса Inventory представлена в листинге 14.11, Можно видеть,

 

 

 

что включен только заголовочный файл "inventory, h". В этом файле используют­

 

 

 

ся объекты типа Item и Customer, но компилятор не выдаст сообщения о том, что

 

 

 

данный тип имен неизвестен. В силу того факта, что заголовочные файлы Item

 

 

 

и Customer включены в файл "inventory, h", они также входят и в реализацию

 

 

 

класса Inventory. Подобно листингам 14.6 и 14.8, в него включены комментарии

 

 

 

к строкам, которые указывают, из какой части серверной программы вызывается

 

 

 

каждый метод. В отличие от классов Item и Customer класс Inventory содержит

 

 

 

только один клиентский класс — класс Store.

 

Листинг 14.11. Реализация класса Inventory (файл inventory, срр)

 

/ /

f i l e

inventory.срр

 

 

#include

<iostream>

 

 

using namespace std;

 

 

#inclucle

"inventory, h"

/ / это необходимость

Inventory::Inventory()

{ itemCount = itemldx = 0; custCount = custldx =0; }

void Inventory::appendltem (const char* ttl, int id, int qty, int cat)

{ if (itemCount == MAXM)

// используется в loadDataO

{cout « "\nNo space to insert item"; }

else

{itemList[itemCount++].set(ttl,id.qty.cat); } }

void Inventory::appendCust (const char* nm, const char* ph,

 

int cnt, const int*movie)

в loadDataO

{ if (custCount == MAXC)

// используется

{ cout « "\nNo space to insert customer";

return; }

 

custList[custCount++].set(nm.ph);

 

 

for (int j=0; j < cnt; j++)

 

 

custList[custCount-1].addMovie(movie[j]); }

 

int Inventory: :getltem(ltem &item)

// используется

в saveDataO

{ if (itemldx == itemCount)

 

 

{ itemldx = 0; return 0; }

 

 

item =itemList[itemIdx++];

 

 

return 1; }

 

 

int Inventory::getCustomer(char* nm, char* ph, int &cnt, int *m)

 

{ if (custldx == custCount)

// в findCustomerO, saveDataO

{ custldx = 0; return 0; }

 

 

custList[custIdx++].getCustomer(nm,ph,cnt,m); return 1; }

Глава 14 • Выбор между наследованием и композицией

627

void Inventory::printRental(int id)

// используется в findCustomer()

 

{ for (itemldx = 0; itemldx < itemCount;

itemldx++)

 

{if (itemList[itemIdx].getId() == id)

{itemList[itemIdx].printItem(); break; } } itemldx = 0;}

int Inventory::checkOut(int

id)

 

// используется

в processItemO

{ for(itemldx = 0; itemldx < itemCount;

itemldx++

 

 

if (itemList[itemIdx].getId() == id) break;

 

if (itemldx == itemCount)

 

 

 

{ itemldx = custldx = 0;

return 0; }

 

 

if (itemList[itemIdx].getQuant() == 0)

 

{ itemldx = custldx = 0;

return 1; }

 

 

itemList[itemIdx].incrQty(-1);

 

 

 

custList [custldx - 1 ].addMovie(id);

 

 

itemldx = custldx = 0;

 

 

 

 

return 2; }

 

 

 

 

 

 

void

Inventory::checkln(int

id)

 

/ / используется

в processltem ()

{ i f

(custList[custIdx - 1]. removeMovie(id) == 0)

 

{

cout «

" Movie is not found\n";

 

 

 

itemldx

= custldx = 0;

return;

}

 

 

for

(itemldx

= 0;

itemldx

< itemCount;

itemldx++)

 

 

{ i f

(itemList[itemIdx].getId()

==

id)

 

 

 

{ itemList[itemIdx].incrQty(1); break; } }

 

itemldx

= custldx

= 0;

 

 

 

 

cout «

" Movie is

returned\n"; }

 

 

 

Конструктор Inventory инициализирует индексы и счетчики компонентов (филь­ мов) и клиентов. Первоначально оба списка были пустыми. Методы appendltem() и appendCustO просты. Они тестируют имеющееся свободное пространство (этот тест соответствует прототипу, но избыточен при динамическом управлении памятью), добавляют компонент в конец массива и увеличивают счетчик действительных компонентов.

Методы getltemO и getCustomerO извлекают данные объекта из массива по указанному индексу (itemldx для объекта Item, custldx ддя объекта Customer). В одном случае извлекается весь объект, а в другом — значения элементов данных объекта. Поэтому в одном случае это клиентская программа, изменяющая значе­ ния элементов набора данных, а в другом случае представлен класс Inventory, который все выполняет по поручению клиентской программы.

Метод printRental() использует идентификатор фильма ддя поиска в массиве itemList[]. Если фильм найден, объекту посылается сообщение printltem().

Метод checkout () с идентификатором фильма в качестве параметра осуществ­ ляет поиск компонента в массиве itemList[]. Если компонент не обнаружен, ра­ бота завершается и возвращается 0. Если компонент найден в данный момент, работа завершается возвращается 1. Если компонент доступен, количество имею­ щихся в наличии компонентов уменьшается на единицу, в список фильмов, взятых напрокат клиентами, добавляется идентификатор фильма и возвращается 2.

В методе checkInO идентификатор фильма также используется как параметр. Поиск компонента в списке фильмов, взятых напрокат клиентами, осуществляет­ ся с помощью вызова метода removeMovie(). Если фильм не найден, checkln() вы­ водит на печать сообщение и завершает работу. Если фильм находится в списке клиента (а затем удаляется из списка), checkInO ищет компонент в массиве компонентов itemList[], увеличивает значение имеющихся в наличии фильмов и выводит на печать подтверждение.

% 628 Л

Часть II! # Програтттрошаиые с агрегированием ш наслвАОваиивт

 

 

 

 

Интерфейсы методов Checkin 0 и checkout () несовместимы. Метод checkout ()

 

 

не вовлечен в диалог интерфейса пользователя. Вместо этого он возвращает зна­

 

 

чение, которое должен проанализировать клиент, и выводит на печать соответст­

splash

 

101

11

с

вующее сообщение. Работа передается клиентской части. Метод

 

checkInO отвечает за анализ состояний с ошибкой и соответст­

Birds

 

102

22

h

вующий пользовательский интерфейс. Он скрывает состояния

Gone with thewind

103

10

f

Рис. 14.8.

 

 

 

 

ошибки от клиентской части и возвращает допустимое значение.

 

 

 

 

Класс File разработан для осуществления доступа к физиче-

Пример входного

файла

 

 

ским файлам, которые содержат данные о фильмах и клиентах

с данными о фильмах

 

 

д^ ^ после выполнения программы. На рис. 14.8 представлен

 

 

 

 

 

пример файла ввода с данными о фильмах. Каждая строка в нем

соответствует одному компоненту и включает заголовок фильма

 

 

353-2566

1

(с выравниванием

по левому краю), идентификационный номер,

Shtern

число имеющихся в наличии фильмов и категорию (буква).

2

101 102

 

Shtern

358-0008

 

На рис. 14.9 можно видеть пример входного файла с данными

0

 

277-7506

 

клиента. Для

каждого клиента

выделяются две строки. Первая

Simons

 

3

102

101 103

 

строка содержит имя и номер телефона клиента. Во второй строке

 

 

 

 

 

 

 

хранится количество фильмов, взятых напрокат данным клиентом,

Рис. 14.9.

 

файла

 

и список номеров доступа к фильмам.

 

пример

входного

 

Входной и выходной файлы имеют одинаковый формат. Инфор­

с данными

о

клиентах

 

 

 

 

 

 

 

 

мационное содержание файла вывода для компонентов, полученное

 

 

 

 

 

 

 

Splash

 

101

12

с

в результате выполнения программы показано на рис. 14.10.

 

Вы видите, что была возвращена одна копия "Splash" и выда­

Birds

 

102

22

h

на одна копия "Gone with the Wind".

 

 

 

 

 

Gone with the wind

103

9

f

 

 

 

 

 

 

 

 

 

 

На рис. 14.11 представлено содержание файла с данными

Р г 1Л

 

 

 

^ клиентах после выполнения программы. В нем указывается,

Пример

выходного

файла

 

 

'^'^^ клиент Штерн возвратил

фильм

с

идентификационным

с данными о фильмах

 

 

номером 101 и взял напрокат фильм с идентификационным

 

 

 

 

 

номером 103.

 

 

 

 

 

 

 

В листинге 14.12 приведены спецификации класса для класса

Shtern

102

 

353-2566

1

File. Этот класс инкапсулирует файловый объект fstream,

2

101

 

358-0008

 

Shtern

 

 

 

 

способный выполнять считывание и запись данных. Класс реализу­

0

 

 

 

277-7506

 

ет общедоступные методы getltemO и saveltemO, выполняющие

Simons

101

103

 

3

102

 

 

операции ввода/вывода для данных Item. Он также реализует

Рис. 14.11.

 

 

 

общедоступные методы getCustomerO и saveCustomerO, которые

 

 

 

выполняют операции ввода/вывода для данных Customer.

Пример

выходного

файла

 

 

 

 

 

 

 

с данными

о

клиентах

 

Л и с т и нг 14.12. Спецификации класса для класса File (файл f i l e . h)

/ / f i l e f i l e . h

#ifnclef FILE_H #clefine FILE_H #inclucle "item.h" #inclucle <fstream>

class File

{fSt ream f;

static void trim(char

buffer [ ] ) ;

enum { TWIDTH = 27,

IWIDTH = 5,

QWIDTH = 6,

 

NWIDTH = 18,

PWIDTH = 16

};

public:

 

 

 

File(const char name[], int mode);

int

getltem(char * t t l ,

int &id,

in &qty, char &type);

void

saveItem(const

Item &item);

Глава 14 • Выбор между наследованием и композицией

629 I

int getCustomer(char *name, char *phone, int &count, int *т); void saveCustomer(const char *nm, const char *ph,

int cnt, int *m) ;

}

#enclif

В листинге 14.13 представлена реализация класса File. Его конструктор от­ крывает физический файл либо для чтения, либо для записи и тестирует прове­ дение операции с помощью функции Fail(). Другой способ протестировать проведение операции — вызвать функцию is_open(), которая возвращает true, если файл успешно открылся.

Листинг 14.13. Реализация класса File (файл file.cpp)

/ /

f i l e file.cpp

 

 

 

# include <iostream>

 

 

 

using

namespace std;

 

 

// это необходимость

# include "file.h"

 

 

File::File(const char name[], int mode)

.// используется в loadDataO, saveDataO

{

f.open (name,mode);

 

 

 

if

(f.failO)

 

 

//также, если (f. is_open()) успешно

 

{ cout « " File is not open\n" ; exit(1);

} }

int File:: getltem(char

*ttl, int &id, int &qty, char &type)

{

char buffer [200];

 

 

// в loadDataO

 

f.get(buffer.TWIDTH);

 

 

 

 

trim(buffer) ;

 

 

// знает структуру файла

 

strcpy(ttl,buffer);

 

 

 

f »

id; f-» qty; f »

 

type ;f.getline(buffer,4);

 

if (!f) return 0;

 

 

 

 

return 1; }

 

 

 

void File::saveItem(const

Item &item)

// в saveDataO

{

char tt[27]; intid, qty, type;

 

 

item.getltem(tt,id,qty,type) ;

 

 

f.setf(ios::left, ios::adjustfield);

// знает формат файла

 

f.width (TWIDTH) ; f «

tt;

 

f.setf (ios: : right, ios: : adjustfield)

 

 

f.width(IWIDTH); f «

id ;

 

 

f.width (QWIDTH) ; f «

qty;

// отличается от других подтипов

 

switch (type) {

 

 

 

case 1: f « '' f\n" break

 

 

case 2: f « '' c\n"break

 

 

case 3: f « '' h\n"

break } }

 

int File::getCustomer(char

*name, char *phone,

int &count, int *m)

{

char buffer[200] ;

 

 

// в loadDataO

 

f.get(buffer, NWIDTH);

 

 

 

 

trim (buffer);

 

 

 

 

strcpy(name, buffer);

 

 

// знает структуру файла

 

f »

buffer; f » count;

 

 

strcpy(phone, buffer);

 

i++)

 

 

for (int i=0; i < count;

 

 

f » m [i];

 

 

 

 

f.getline(buffer,2);

 

 

 

Соседние файлы в предмете Программирование на C++