- •Р. Лафоре
- •Глава 1. Общие сведения 32
- •Глава 3. Циклы и ветвления 92
- •Глава 4. Структуры 142
- •Глава 5. Функции 168
- •Глава 6. Объекты и классы 217
- •Глава 7. Массивы и строки 261
- •Глава 8. Перегрузка операций 312
- •Глава 9. Наследование 361
- •Глава 10. Указатели 411
- •Глава 11. Виртуальные функции 476
- •Глава 12. Потоки и файлы 536
- •Глава 13. Многофайловые программы 596
- •Глава 14. Шаблоны и исключения 640
- •Глава 15. Стандартная библиотека шаблонов (stl) 681
- •Глава 16. Разработка объектно-ориентированного по 752
- •Глава 1 «Общие сведения» включает список тем, касающихся uml, с указа- нием их расположения в книге.
- •Глава 1
- •Глава 2
- •Глава 3
- •If внутри циклов
- •If и else во вложенных ветвлениях
- •Глава 4
- •Глава 5 Функции
- •Глава 6
- •Глава 7
- •123456. Россия
- •123456. Россия
- •Глава 8
- •Глава 9
- •Глава 10 Указатели
- •Main() передает адрес переменной var в ptrd в centimize()
- •Centimize() использует этот адрес для доступа к var
- •Глава 11
- •Глава 12
- •Тип:менеджер Фамилия: Александров Номер:1111
- •Тип:Ученый Фамилия: Лебедев Номер:2222
- •Тип:рабочий Фамилия:Шевелев Номер:3333
- •Глава 13
- •Глава 14
- •Много объектов разных классов в памяти Рис. 14.2. Шаблон класса
- •Алгоритмы используют итераторы для работы с объектами контейнеров. Рис. 15.1. Контейнеры, алгоритмы и итераторы
- •Глава 16
- •Глава 1
- •Глава 2
- •Глава 3
- •Глава 4
- •Глава 5
- •Глава 6
- •Глава 7
- •Глава 8
- •Глава 9
- •Глава 10 Ответы на вопросы
- •Глава 11
- •Глава 12 Ответы на вопросы
- •Глава 13 Ответы на вопросы
- •Глава 14 Ответы на вопросы
- •Глава 15 Ответы на вопросы
- •Глава 16
'x'
-
выход
Ваш
выбор: а
'm'
для добавления менеджера
's'
для добавления ученого
'l'
для добавления рабочего
Ваш
выбор: s
Введите
фамилию: Лебедев
Введите
номер:2222
Введите
число публикаций: 99
'a'
- добавление сведений о работнике
'd'
- вывести сведения обо всех работниках
'w'
- записать все данные в файл
'r'
- прочитать все данные из файла
'x'
- выход
Ваш
выбор: а
'm'
для добавления менеджера
's'
для добавления ученого
'l'
для добавления рабочего
Ваш выбор:
l
Введите
фамилию: Шевелев
Введите номер:3333
'a'
- добавление сведений о работнике
'd'
- вывести сведения обо всех работниках
'w'
- записать все данные в файл
'r'
- прочитать все данные из файла
'x'
- выход
Ваш
выбор: w
Идет
запись 3 работников
'a'
- добавление сведений о работнике
'd'
- вывести сведения обо всех работниках
'w'
- записать все данные в файл
'r'
- прочитать все данные из файла
'x'
- выход
Ваш
выбор: r
Идет
чтение 3 работников
'a'
- добавление сведений о работнике
'd'
- вывести сведения обо всех работниках
'w'
-
записать все данные в файл
'r'
- прочитать все данные из файла
'x'
- выход
Ваш
выбор: d
Титул
Президент
Налоги гольф-клуба: 20000
Число
публикаций: 99
Тип:менеджер Фамилия: Александров Номер:1111
Тип:Ученый Фамилия: Лебедев Номер:2222
Тип:рабочий Фамилия:Шевелев Номер:3333
Конечно же, можно выйти из программы и
после записи на диск. Когда вы
повторно
запустите программу, все данные снова
появятся и смогут быть про-
читаны.
Эту программу легко расширить за счет
добавления функций удаления ра-
ботника,
извлечения данных об одном конкретном
работнике, поиска работника
с
конкретными характеристиками и т. д.
Перегрузка
операторов извлечения
и вставки
Давайте перейдем
к изучению следующей темы, связанной
с потоками. Данный
раздел будет
посвящен перегрузке операторов
извлечения и вставки. Это мощ-
ный
прием C++,
который позволяет поддерживать
ввод/вывод
пользователь-
ских типов наравне со
стандартными, такими, как float
и int.
Например,
если
имеется объект класса crawdad,
называющийся
cd1,
его можно
вывести на экран
простым выражением
cout
<<
"\ncd1
="
<<
cd1;
Как видите, оно ничем не отличается от
подобных конструкций для стан-
дартных
типов.
Операторы извлечения
и вставки могут быть перегружены и для
работы с
консольным вводом/выводом
(с
клавиатуры и на экран — cin
и cout).
С огляд-
кой,
но можно перегрузить их и для работы с
дисковыми файлами. Мы рассмот-
рим
примеры всех этих ситуаций.
Перегрузка
cout
и
cin
Приведем пример,
в котором операторы извлечения и вставки
для класса Distance
перегружены
для работы с cout
и cin.
Листинг
12.18. Программа ENGLIO
//
englio.cpp
//
Перегружаемые операции << и >>
#include
<iostream>
using
namespace std;
///////////////////////////////////////////////////////////
class
Distance
//
класс английских расстояний
{
private:
int
feet;
float
inches;
public:
Distance()
:feet(0), inches(0.0) //
конструктор (0 арг.)
{
}
//
конструктор (2 аргумента)
Distance(int
ft, float
in) : feet(ft), inches(in)
{
}
Листинг
12.18
(продолжение)
friend
istream&
operator>>(istream&
s, Distance& d);
friend
ostream&
operator<<(ostream&
s, Distance& d);
};
//---------------------------------------------------------
istream&
operator>>(istream&
s, Distance& d) //
получить
//
значение от пользователя
{
cout
<< "\nВведите
футы: ";
s
>> d.feet;
//
используется
cout
<< "Введите
дюймы: ";
s
>> d.inches;
//
перегруженный
return
s; //
оператор >>
}
//---------------------------------------------------------
ostream&
operator<<(ostream&
s, Distance& d) //
вывести
{
//
расстояние
s
<< d.feet
<< "\'-"
<<
d.inches
<<
'\"';
//
используется
return
s; //
перегруженный <<
}
///////////////////////////////////////////////////////////
int
main()
{
Distance
dist1,
dist2;
//
Определение переменных
Distance
dist3(11,
6.25); //
определение, инициализация dist3
cout
<< "\nВведите
два значения расстояний:";
cin
>>
dist1
>> dist2;
//
Получить значения от пользователя
//
вывод расстояний
cout
<< "\ndist1
= "
<< dist1 << "\ndist2
= "
<< dist2;
cout
<< "\ndist3
= "
<< dist3 << endl;
return
0;
}
Программа запрашивает
у пользователя два значения расстояний
(типа
Distance),
выводит их, а
также выводит значения, которые были
инициализиро-
ваны в программе.
Введите
футы:10
Введите дюймы:3.5
Введите
футы:12
Введите дюймы:6
dist1
= 10'-3.5"
dist2
= 12'-6"
dist3
= 11'-6.25"
Обратите внимание,
как удобно и естественно можно работать
с объектами
типа Distance,
— совершенно
так же, как с объектами любого другого
типа:
cin
>>
dist1
>>
dist2;
Или,
например:
cout
<< "\ndist1
= "
<< dist1 << "\ndist2
= "
<< dist2;
Операторы извлечения
и вставки перегружаются одинаковыми
способами.
Они
возвращают по ссылке объект istream
(для
>>)
или ostream
(для
<<).
Возвра-
щаемые
значения могут организовываться в
цепочки. Операторы имеют по два
аргумента,
оба передаются по ссылке. Первый аргумент
>>
— объект класса
istream
(например,
cin).
Соответственно,
для <<
первым аргументом должен
быть
объект класса
ostream
(например,
cout).
Второй аргумент
— объект класса, для
которого
осуществляется ввод/вывод
(в
данном примере это класс Distance).
Оператор
>>
берет входные данные из потока, указанного
в первом аргументе,
и переносит их
в компонентные данные объекта, указанного
во втором. По анало-
гии оператор <<
берет данные из объекта, указанного во
втором аргументе, и по-
сылает их в
поток, соответствующий значению первого
аргумента.
Функции operator<<()
и operator>>()
должны быть
дружественными по отно-
шению к
классу Distance,
так как объекты
istream
и ostream
находятся слева
от
знака операции (см.
обсуждение дружественных функций в
главе 11).
Разумеется, для любых других классов
можно перегружать операторы встав-
ки
и извлечения таким же способом.
Перегрузка
<<
и >>
для файлов
Наш следующий
пример продемонстрирует, как перегружаются
операторы <<
и >>
в
классе Distance
для работы с
файловым вводом/выводом.
Листинг
12.19. Программа ENGLIO2
//
englio2.cpp
//
перегружаемые операции << и >>
могут работать с файлами
#include
<fstream>
#include
<iostream>
using
namespace std;
class
Distance //
класс английских расстояний
{
private:
int
feet;
float
inches;
public:
//
конструктор (без аргументов)
Distance()
: feet(0), inches(0.0)
{
}
//
конструктор (2-арг)
Distance(int
ft, float
in) : feet(ft), inches(in)
{
}
friend
istream&
operator>>(istream&
s, Distance& d);
friend
ostream&
operator<<(ostream&
s, Distance& d);
};
//---------------------------------------------------------
//
получить
данные
из
файла или с клавиатуры
//
для ('), (-) и (")
с
помощью перегруженного >>
istream&
operator>>(istream&
s, Distance& d)
{
char
dummy;
s
>> d.feet >> dummy >> dummy >> d.inches >>
dummy;
return
s;
}
//---------------------------------------------------------
//
послать
данные
типа Distance в файл или на экран перегруженным
<<
ostream&
operator<<(ostream&
s, Distance& d)
{
s
<< d.feet << "\'-"
<<
d.inches <<
'\"';
return
s;
}
Листинг
12.19 (продолжение)
///////////////////////////////////////////////////////////
int
main()
{
char
ch;
Distance
dist1;
ofstream
ofile; //
создать
и
открыть
ofile.open("DIST.DAT");
//
выходной поток
do
{
cout
<< "\nВведите
расстояние: ";
cin
>>
dist1;
//
получить данные от пользователя
ofile
<< dist1;
//
записать их в выходной поток
cout
<< "Продолжать
(y/n)?
";
cin
>>
ch;
}
while(ch
!= 'n');
ofile.close();
//
закрыть выходной поток
ifstream
ifile;
//
создать и открыть
ifile.open("DIST.DAT");
//
входной поток
cout
<< "\nСодержимое
файла:\n";
while(true)
{
ifile
>> dist1;
//
чтение данных из потока
if(ifile.eof())
//
выход по EOF
break;
cout
<< "Расстояние
= "
<< dist1 << endl;
//
вывод
//
расстояний
}
return
0;
}
В сами перегружаемые
операции мы внесли лишь минимальные
изменения.
Оператор >>
больше не просит ввести входные данные,
потому что понимает,
что бессмысленно
просить о чем-либо файл. Мы предполагаем,
что пользователь
знает, как точно
вводить значения футов и дюймов, включая
знаки пунктуации.
Оператор <<
остался без изменений. Программа
запрашивает у пользователя
входные
данные и после получения каждого
значения записывает его в файл.
Когда
пользователь оканчивает ввод данных,
программа читает из файла и вы-
водит
на экран все хранящиеся там значения.
Вот пример работы программы:
Введите
расстояние: 3'-4.5"
Продолжать (y/n)?
у
Введите
расстояние: 7'-11.25"
Продолжать
(y/n)?
у
Введите
расстояние: 11'-б"
Продолжать
(y/n)?
n
Содержимое
файла:
Расстояние =
3'-4.5"
Расстояние =
7'-11.25"
Расстояние =
11'-б"
Значения расстояний записаны в файле
символ за символом. Поэтому реаль-
ное
содержание файла таково:
3'-4.5"7'-11.25"11'-6"
Если пользователь
ошибется при вводе данных, они не будут
записаны кор-
ректно в файл и не
смогут быть прочитаны оператором <<.
В реальной програм-
ме необходимо
проверять, правильно ли производится
ввод.
Память
как поток
Область памяти
можно считать потоком и записывать в
нее данные точно так же,
как в файл.
Это требуется, когда нужно выводить
данные в определенном фор-
мате
(например,
оставлять только два знака после
запятой)
и одновременно ис-
пользовать функцию
текстового вывода, которая в качестве
аргумента требует
строку. Так обычно
делают, вызывая функции вывода, при
разработке приложе-
ний в GUI-средах,
например в Windows,
поскольку там
этим функциям нужно
передавать
именно строку в качестве аргумента.
Программисты на C, вероятно,
сразу
вспомнят, как они использовали sprintf()
для этих целей.
Семейство потоковых
классов поддерживает такое форматирование
данных
в памяти. Для вывода в память
существует специальный класс ostrstream,
порож-
денный
классом ostream.
Для ввода из
памяти служит класс istrstream,
порожден-
ный,
соответственно, классом istream.
Для объектов,
которым требуется осуще-
ствлять
одновременно ввод и вывод, создан класс
strstream
— наследник
iostream.
Скорее всего, вы
захотите использовать ostrstream.
Наш
следующий пример
показывает, как
это реально применить на практике.
Начните с создания буфера
данных в
памяти. Затем сваяйте объект ostrstream,
используя этот
буфер (его
ад-
рес и размер)
в качестве аргументов конструктора
потока. Теперь можно выво-
дить
форматированный текст в буфер, как если
бы он был потоковым объектом.
Листинг
12.20. Программа OSTRSTR
//
ostrstr.cpp
//
Запись форматированных данных в память
#include
<strstream>
#include
<iostream>
#include
<iomanip>
//
для функции setiosflags()
using
namespace std;
const
int
SIZE = 80; //
размер буфера
int
main()
{
char
ch = 'x';
//
тестовые данные
int
j = 77;
double
d = 67890.12345;
char
str1[] = "Kafka";
char
str2[] = "Freud";
char
membuff[SIZE]; //
буфер в памяти
ostrstream
omem(membuff, SIZE); //
создать потоковый объект
Листинг
12.20 (продолжение)
omem
<< "ch
= "
<< ch
<< endl
//
вставить форматированные данные
<<
"j
= "
<< j
<< endl
//
в объект
<<
setiosflags(ios::fixed)
//
формат с десятичной запятой (фиксированной)
<<
setprecision(2)
//
оставлять два знака после запятой
<<
"d
= "
<< d << endl
<<
"str1
= "
<< str1 << endl
<<
"str2
= "
<< str2
<< endl
<<
ends;
//
закончить буфер символом '\0'
cout
<< membuff;
//
вывод содержимого буфера
return
0;
}
После запуска
программы membuff
заполнится
форматированным текстом:
ch=x\nj=77\nd=67890.12\nstr1=Kafka\nstr2=Freud\n\0
Можно форматировать
данные с плавающей запятой привычными
способа-
ми. Здесь же мы используем
формат с фиксированной запятой (не
экспоненци-
альный),
указав на это с помощью ios::fixed,
при этом
оставляем два знака после
запятой.
Манипулятор ends
вставляет в
конец строки служебный символ '\0'
для
создания признака EOF.
Вывод на экран
содержимого буфера с использованием
обычного
cout
дает такой
результат:
ch
= x
j =
77
d
= 67890.12
str1 = Kafka
str2 = Freud
Здесь выводится содержимое буфера
просто для демонстрации того, что
кое-
что из написанного в нашей
программе работает. Вообще же, конечно,
такие ме-
тоды используются для более
сложных операций с данными.
Аргументы
командной строки
Если вы использовали
когда-нибудь старый добрый MS
DOS,
вам должно
быть
знакомо понятие аргументов
командной строки, использующихся при
запуске
программ. Их типичное
применение — передача имени файла с
данными в при-
ложение. Например, вы
запускаете программу редактирования
текстов и хотите
сразу открыть
документ, с которым будете работать.
Для этого вы пишете:
C:\>wordproc
afile.doc
Здесь аргументом
командной строки является afile.doc.
Нам надо
каким-то
образом заставить программу
на C++
распознавать такие обращения. Следу-
ющая
программа, приводимая в качестве
примера, считывает и выводит на эк-
ран
столько аргументов командной строки,
сколько вы сможете напечатать
(они
отделяются
пробелами).
Листинг
12.21.
Программа COMLINE
//
comline.cpp
//
Демонстрация работы с аргументами
командной строки
#include
<iostream>
using
namespace std;
int
main(int
argc, char*
argv[])
{
cout
<< "\nargc
= "
<< argc << endl;
//
число аргументов
for(int
j = 0; j < argc; j++) //
вывести аргументы
cout
<< "Аргумент
"
<< j << "
= "
<< argv[j] << endl;
return
0;
}
А
вот пример работы этой программы:
C:\C++BOOK\Chap12>comline
uno dos tres
argc
= 4
Аргумент
0 = C:\C++BOOK\CHAP12>COMLINE.exe
Аргумент
1 = uno
Аргумент
2 = dos
Аргумент
3 = tres
Для того чтобы
программа смогла прочитать аргументы,
функции main()
(не
забываем,
что это тоже функция!)
должны быть самой переданы два
аргумента.
Первый, argc
(счетчик
аргументов),
представляет собой число параметров,
пере-
данных из командной строки в
программу. Первый параметр — это всегда
пол-
ный путь к данной программе.
Остальные уже зависят от того, что ввел
пользо-
ватель. Друг от друга аргументы
отделяются пробелом. В приведенном
примере
аргументами командной строки
были uno,
dos,
tres.
Система хранит
переданные ей параметры как строки в
памяти. Создает мас-
сив указателей
на эти строки. В нашем примере этим
массивом был argv.
Каж-
дая
отдельная строчка доступна с помощью
соответствующего указателя, поэто-
му
первым аргументом (путем
к программе)
является argv[0],
вторым —
argv[1]
и
т. д, COMLINE
обращается по
очереди к аргументам и выводит их на
экран с по-
мощью цикла for,
использующего
в качестве ограничителя параметр argc
(число
переданных
в программу аргументов).
Нет особой нужды
использовать только имена argv
и argc
в качестве
парамет-
ров main(),
но они настолько
привычны, что другие названия вызовут
у всех,
кроме невозмутимого компилятора,
повышенное напряжение мозговой
деятель-
ности в попытках понять,
чего хотел программист.
Приводимая ниже
программа использует аргумент командной
строки для че-
го-то действительно
полезного. Она выводит на экран содержимое
текстового
файла, имя которого
пользователь ввел в командной строке.
Таким образом, она
имитирует команду
TYPE
MS
DOS.
Листинг
12.22.
Программа OTYPE
//
otype.cpp
//
Имитация команды TYPE
#include
<fstream> //
для
файловых
функций
#include
<iostream>
Листинг
12.22 (продолжение)
using
namespace std;
#include
<process.h>
//
для exit()
int
main(int
argc, char*
argv[])
{
if(argc
!= 2)
{
cerr
<< "\nФормат
команды: otype имя_файла";
exit(-1);
}
char
ch;
//
символ для считывания
ifstream
infile;
//
создать входной файл
infile.open(argv[1]);
//
открыть файл
if(!infile)
//
проверить на наличие ошибок
{
cerr
<< "\nНевозможно
открыть "
<< argv[1];
exit(-1);
}
while(infile.get(ch)
!= 0) //
считать символ
cout
<< ch;
//
отобразить символ
return
0;
}
Программа вначале
проверяет, правильное ли количество
аргументов ввел
пользователь. Надо
при этом помнить, что путь к файлу
OTYPE.EXE
всегда
будет
первым аргументом. Вторым —
путь к файлу, который пользователь
собирается
открыть.
C:\>otype
ichar.cpp
Таким образом,
всего аргументов должно быть ровно
два.
Если это не так,
вероятно, пользователь
еще не понял, как работать с нашей
великой програм-
мой. Чтобы прояснить
для него ситуацию, на экран выводится
специальное со-
общение с помощью
cerr.
Если количество
аргументов сошлось с требуемым, программа
начинает пы-
таться открыть указанный
файл (argv[1]).
Опять же, если
с этим возникли какие-
то проблемы,
программа выводит соответствующее
сообщение. Наконец, после
корректного
открытия, в цикле while
считывается
символ за символом весь файл
и
выводится на экран.
Нулевое значение
символа говорит о том, что достигнут
конец файла. Это —
еще один способ
распознавания EOF.
Так же можно
использовать значение само-
го
файлового объекта, как мы делали ранее:
while(infile)
{
infile.get(ch);
cout
<<
ch;
}
А вы знаете, что
весь этот цикл while
можно заменить
одним-единственным
выражением?
cout
<<
infile.rdbuf();
Между тем такой
способ мы уже видели в программе ICHAR2.
Вывод
на печатающее устройство
Не составляет никаких проблем использовать
консольные программы для того,
чтобы
посылать данные на принтер. Операционная
система создает ряд специ-
альных
имен файлов, которые обозначают различные
устройства. Тем самым
делается
возможной работа с устройствами как с
файлами. Таблица 12.11 содер-
жит все
зарезервированные под устройства имена
файлов.
Таблица
12.11.
Имена устройств
Имя |
Устройство |
con |
Консоль (клавиатура и монитор) |
aux или com1 |
Первый последовательный порт |
com2 |
Второй последовательный порт |
prn или lpt1 |
Первый параллельный порт |
lpt2 |
Второй параллельный порт |
lpt3 |
Третий параллельный порт |
nul |
Фиктивное (несуществующее) устройство |
В большинстве систем принтер подключен к первому параллельному порту, поэтому имя принтера — prn или lpt1 (понятно, что в случае, если ваша система настроена иначе, надо использовать другое имя).
Следующая программа посылает строку и число на принтер, используя фор- матированный вывод (оператор вставки).
Листинг 12.23. Программа EZPRINT
// ezprint.cpp
// Простой вывод на принтер
#include <fstream> // Для файловых потоков
using namespace std;
int main()
{
char* s1 = "\nСегодня ваше счастливое число -- ";
int n1 = 17982;
ofstream outfile; // создать выходной файл
outfile.open("PRN"); // открыть принтеру доступ к нему
outfile << s1 << n1 << endl; // послать данные на принтер
outfile << '\x0C'; // прогнать лист до конца
return 0;
}
Таким способом на принтер можно послать сколько угодно строк. Служеб- ный символ '\ x0C ' осуществляет прогон страницы.
Следующая программа распечатает для вас содержимое дискового файла на принтере. В ней используется посимвольный подход к передаче данных.
Листинг 12.24. Программа OPRINT
// oprint.cpp // имитация команды print
Листинг 12.24 (продолжение)
#include <fstream> // для файловых функций
#include <iostream>
using namespace std;
#include <process.h> // для exit()
int main(int argc, char* argv[])
{
if(argc != 2)
{
cerr << "\nФормат команды: oprint имя_файла\n";
exit(-1);
}
char ch; // символ для считывания
ifstream infile; // создать входной файл
infile.open(argv[1]); // открыть файл
if(!infile) // проверить на наличие ошибок
{
cerr << "\nНевозможно открыть " << argv[1] << endl;
exit(-1);
}
ofstream outfile; // Создать файл
outfile.open("PRN"); // открыть доступ принтера к нему
while(infile.get(ch) != 0)// считать символ
outfile.put(ch); // отправить символ на печать
outfile.put('\x0C'); // прогон страницы
return 0;
}
Эта программа может быть использована для печати любых текстовых фай- лов, например исходных текстов программ .cpp. Она очень похожа на команду print из операционной системы MS DOS. Как и предыдущие, программа прове- ряет корректность количества аргументов и правильность открытия файла.
Резюме
В этой главе мы ознакомились с иерархией потоковых классов и показали, как обрабатывать различного рода ошибки ввода/вывода. Затем мы рассмотрели некоторые варианты файлового ввода/вывода. Файлы в C++ связаны с объек- тами различных классов: класс ofstream используется для файлового вывода, ifstream — для ввода, fstream — для ввода и вывода одновременно. Методы этих или базовых классов предназначены для выполнения операций ввода/вывода. Такие операции и функции, как <<, put() и write(), используются для вывода, а >>, get() и read() — для ввода.
Функции read() и write работают с данными в двоичном режиме. Поэтому мож- но записывать в файлы объекты целиком, вне зависимости от типов данных, ко- торые они содержат. Могут храниться как отдельные объекты, так и массивы и другие структуры, составленные из множества объектов. Файловый ввод/вы- вод может обрабатываться с использованием методов. За него могут отвечать как конкретные объекты, так и классы (с помощью статических функций).
Проверка на наличие
ошибок должна осуществляться после
выполнения каж-
дой файловой операции.
Сам файловый объект принимает нулевое
значение, ес-
ли возникает какая-либо
ошибка. К тому же для определения
некоторых видов
ошибок используются
методы классов. Операции извлечения
(>>)
и вставки (<<)
перегружаются
для работы с пользовательскими типами
данных. Память может
представляться
в виде потока, а данные в нее могут
посылаться так же, как если
бы это
был файл.
Вопросы
Ответы на данные вопросы можно найти
в приложении Ж.
Поток C++:
а) представляет
собой поток функционального управления;
б) представляет
собой поток данных из одного места в
другое;
в) ассоциирован
с конкретным классом;
г) представляет
собой файл.
Базовым для
большинства потоковых классов является
класс .
Назовите три
потоковых класса, предназначенных для
файлового ввода/
вывода.
Напишите выражение,
создающее объект salefile
класса ofstream,
и ассо-
циируйте
его с файлом SALES.JUN.
Истинно ли утверждение о том, что
некоторые потоки являются входны-
ми,
а некоторые — выходными?
Напишите if,
определяющий,
достиг объект ifstream
под названием
foobar
конца
файла или же возникла ошибка.
Мы можем выводить
текст в объект класса ofstream
с
использованием
оператора вставки
<<
потому, что:
а) класс
ofstream
— это поток;
б) оператор
вставки работает с любыми классами;
в) на
самом деле вывод осуществляется в cout;
г) оператор
вставки перегружен в ofstream.
Напишите выражение,
записывающее единичный символ в объект
fileOut
класса
ofstream.
Для записи данных,
содержащих переменные типа float,
в объект
типа
ofstream
необходимо
использовать:
а) оператор
вставки;
б) seekg();
в) write();
г) put().
10. Напишите
выражение, считывающее содержимое
объекта ifile
класса ifstream,
в массив buff.
11. Биты
режимов, такие, как app и ate:
а) определяются
в классе ios;
б) могут
устанавливать, для чтения или для записи
открыт файл;
в) работают
с функциями put()
и get();
г) устанавливают
режимы открытия файлов.
Дайте определение
термину
текущая позиция
в контексте работы с фай-
лами.
Истинно ли утверждение о том, что
файловый указатель всегда содержит
адрес
файла?
Напишите выражение,
сдвигающее текущую позицию на 13 байтов
назад
в потоковом объекте f1.
Выражение
f1.write((chav*)&obj1,
sizeof(obj1));
а) записывает
методы obj1
в f1;
б) записывает
данные obj1
в f1;
в) записывает
методы и данные obj1
в f1;
г) записывает
адрес obj1
в f1.
Аргументы командной строки:
а) это
разборки в армии;
б) набираются
после названия программы в командной
строке;
в) делаются
доступными с помощью аргументов main();
г) доступны
только дисковым файлам.
Что означает флаг
skipws
при его
использовании с cin?
Напишите описатель
для main(),
позволяющий
программе распознавать
аргументы
командной строки.
В консольных программах доступ к
принтеру осуществляется с
помощью
зарезервированного имени .
Напишите описатель
перегруженного оператора >>,
который берет данные
из объекта
класса istream
и выводит их
как данные объекта класса Sample.
Упражнения
Решения к упражнениям,
помеченным знаком *,
можно найти в приложении Ж.
*1.
Рассмотрите класс Distance
из программы
ENGLCON,
глава 6 «Объекты
и
классы».
Используя цикл, аналогичный приводимому
в программе DISKFUN
в
этой главе, получите несколько значений
от пользователя и запишите
их
в файл. Добавьте их к уже записанным
там данным (при
их наличии).
При
окончании пользователем ввода прочитайте
файл и выведите на эк-
ран
все значения.
*2.
Напишите программу, эмулирующую команду
COPY
(MS
DOS).
То есть
программа
должна копировать содержимое одного
файла в другой. Долж-
но использоваться
два аргумента командной строки —
исходный файл и
файл назначения.
Например:
С:\>оcopy
srcfile.cpp
destfile.cpp
Осуществляйте проверку числа аргументов
командной строки и возмож-
ность
открытия указанных пользователем
файлов.
*3.
Напишите программу, возвращающую размер
файла, указанного в команд-
ной
строке:
C:\>filesize
program.ext.
В цикле запрашивайте
у пользователя данные, состоящие из
имени, отчест-
ва, фамилии и номера
работника (типа
unsigned
long).
Затем
осуществите
форматированный вывод
в объект ofstream
с помощью
оператора вставки
(<<).
Не забывайте, что строки данных должны
оканчиваться пробелами
или другими
разделителями. Когда пользователь
сообщит об окончании
ввода, закройте
объект ofstream,
откройте
объект ifstream,
прочитайте
и вы-
ведите на экран все данные из
файла, после чего завершите программу.
Создайте класс
time,
включающий
себя целые значения часов, минут и
се-
кунд. Напишите метод get_time(),
спрашивающий
время у пользователя,
и метод
put_time(),
выводящий
время в формате 12:59:59. Внесите в
функ-
цию get_time()
проверку на
ошибки, чтобы минимизировать
возможность
неправильного ввода
пользователем. Эта функция должна
отдельно спра-
шивать часы, минуты
и секунды, проверяя каждое введенное
значение на
наличие флагов ошибок
ios,
а также
проверяя, укладывается ли значение
в
заданный диапазон. Для часов диапазон
составляет от 0 до 23, а для ми-
нут
и секунд — от 0 до 59. Не вводите данные
в виде символьных строк с
последующим
конвертированием. Вводите значения
сразу же как int.
Это
означает,
что вы не сможете выявлять записи с
ненужными здесь деся-
тичными
запятыми, но это в данной программе
не так важно.
В main()
используйте
цикл для получения значений времени
от пользова-
теля функцией get_time(),
затем для их
вывода функцией put_time();
Введите
часы: 8
Введите минуты: 2
Введите
секунды: 39
Время = 8:02:39
Продолжить
(y/n)?
у
Введите
часы: 28
Значение
часов должно лежать между 0 и 23!
Введите
часы: 1
Введите
минуты: 10
Введите
секунды: пять
Неправильно
введены секунды!
Введите
секунды: 5
Время
=
1:10:05
Создайте класс
name,
включающий
в себя данные из упражнения 4
(имя,
отчество,
фамилия и номер работника).
Создайте методы для этого класса,
осуществляющие
файловый ввод/вывод
данных указанного класса (с
ис-
пользованием ofstream
и ifstream).
Используйте
форматирование данных
(операторы
<<
и >>).
Функции чтения и записи должны быть
независи-
мыми: в них необходимо
внести выражения для открытия
соответству-
ющего потока, а также
чтения и записи данных.
Функция записи
может просто добавлять данные в конец
файла. Функ-
ции чтения потребуется
некоторое условие выборки конкретной
записи.
Можно вызывать ее с параметром,
представляющим собой номер записи.
Но
как, даже зная, какую запись следует
читать, функция найдет ее? Ис-
пользование
seekg()
тут не поможет,
так как при форматированном вво-
де/выводе
все записи имеют разные размеры (в
зависимости от количества
символов
в строке и разрядности числа).
Поэтому придется просто счи-
тывать
записи подряд, пока не будет найдена
нужная.
В main()
вставьте вызовы
описанных выше методов, чтобы
пользователь
мог ввести данные с их
последующей записью в файл. Затем
программа
должна выполнить чтение
и продемонстрировать результаты этого
чтения
на экране.
Другим подходом к добавлению файлового
потока к объекту является
превращение
самого этого потока в статическую
компоненту объекта. Для
чего это
делается? Ну, часто бывает проще
представить себе поток свя-
занным
с классом в целом, а не с отдельными
его объектами. К тому же,
гораздо
правильнее открывать поток только
один раз для записи и чтения
всего,
что нужно. Например, мы открываем файл
и начинаем последова-
тельное
чтение. Функция чтения всякий раз
возвращает данные для сле-
дующего
объекта. Указатель файла сдвигается
при этом автоматически,
так как
между чтениями файл мы не закрываем.
Перепишите программы
из упражнений 4 и 6 таким образом, чтобы
ис-
пользовать объект fstream
в качестве
статической компоненты класса
name.
Функционирование
программы должно сохраниться. Напишите
статиче-
скую функцию для открытия
потока и еще одну — для сбрасывания
фай-
лового указателя и установки
его на начало файла. Эту же функцию
мож-
но использовать для чтения всего
файла.
Основываясь на
программе LINKLIST
из главы 10
«Указатели», напиши-
те программу,
позволяющую пользователю выбрать
одно из четырех дей-
ствий нажатием
соответствующей кнопки. Действия
таковы:
добавить ссылку
в список (от
пользователя требуется ввести
целое
число)
показать данные по всем ссылкам из
списка;
записать в файл
данные для всех ссылок (создание
или переписывание
файла);
считать все данные из файла и создать
новый список ссылок, куда и по-
местить
их.
Первые два действия
могут использовать методы, уже имеющиеся
в LINKLIST.
От
вас требуется написать функции для
чтения и записи файла. И для то-
го, и для другого можно использовать
один и тот же файл. В нем должны
храниться
только данные; нет никакого смысла
хранить содержимое указа-
телей,
которые, возможно, уже не будут нужны
во время чтения списка.
9. Начните с упражнения
7 главы 8 «Перегрузка операций» и
перегрузите
операторы извлечения
(>>)
и вставки (<<)
для класса frac
в нашем
кальку-
ляторе с четырьмя действиями.
Имейте в виду, что операторы могут
свя-
зываться в цепочки, поэтому при
выполнении действий с дробями
пона-
добится только одно выражение;
cin
>>
frac1
>>
op
>>
frac2;
Добавьте к
упражнению 9 проверку на наличие ошибок
в операторе из-
влечения (>>).
Но при этом, видимо, потребуется
запрашивать сначала
первую дробь,
затем оператор, затем вторую дробь.
Одним выражением,
как в упражнении
9, уже будет не обойтись. Вывод сообщений
об ошиб-
ках сделает работу с
программой более понятной.
Введите
первую дробь: 5/0
Знаменатель не может
быть нулевым!
Введите первую дробь
заново: 5/1
Введите оператор (+,
-,
*,
/):
+
Введите вторую дробь: одна треть
Ошибка
ввода
Введите
вторую дробь заново: 1/3
Ответ:
16/3
Продолжить
(y/n)?
n
Как показывает
этот пример, необходимо следить за
флагами ошибок ios
и
за тем, чтобы знаменатель не был равен
нулю. Если возникает ошибка,
пользователю
должно быть предложено ввести данные
еще раз.
Начните с класса
bMoney, который мы последний раз видели
в упражне-
нии 5 главы 11. Перегрузите
операторы извлечения и вставки, чтобы
мож-
но было осуществлять ввод/вывод
объектов этого класса. Выполните
ка-
кой-нибудь ввод/вывод
в main().
К программе EMPL_IO
из этой главы
добавьте возможность поиска работ-
ника
в списке, хранящемся в файле, по номеру.
При нахождении совпа-
дения нужно
вывести данные об этом работнике на
экран. Пользователь
должен иметь
возможность запустить функцию поиска
нажатием клави-
ши 'f'.
У пользователя
спрашивается номер работника. Подумайте
над во-
просом, какой должна быть
эта функция — статической, виртуальной
или
какой-то еще? Поиск и вывод на
экран не должны пересекаться с данны-
ми
в памяти.
ПРИМЕЧАНИЕ
Не
пытайтесь прочитать файл, созданный
программой EMPL_IO.
Классы
в программах раз-
ные благодаря
методу find()
в
новой программе, и проблемы, которые
могут возникнуть
в случае, если их
данные смешаются, уже обсуждались в
этой главе. Вам может понадо-
биться
включить параметр RTTI
в
компиляторе. Чтобы не возникало вопросов,
следуйте
указаниям, данным в приложении
В «Microsoft
Visual
C++»
и в приложении Г «Borland
C++
Builder»
(выбирайте
то, что для вас актуальней).
