<< Пред. стр. 97 (из 121) След. >>
Class ZooAnimal {public:
// преобразование: ZooAnimal ==> const char*
operator const char*();
// ...
};
extern void release( const char* );
extern void release( const Bear& );
ZooAnimal za;
// za ==> const char*
// правильно: release( const char* )
release( za );Ошибка! Закладка не определена.Ошибка! Закладка не определена.Ошибка! Закладка не определена.
Упражнение 19.9
Дана такая иерархия классов:
class Base1 {
public:
ostream& print();
void debug();
void writeOn();
void log( string );
void reset( void *);
// ...
};
class Base2 {
public:
void debug();
void readOn();
void log( double );
// ...
};
class MI : public Base1, public Base2 {
public:
ostream& print();
using Base1::reset;
void reset( char * );
using Base2::log;
using Base2::log;
// ...
};
Какие функции входят в множество кандидатов для каждого из следующих вызовов:
MI *pi = new MI;
(a) pi->print(); (c) pi->readOn(); (e) pi->log( num );
(b) pi->debug(); (d) pi->reset(0); (f) pi->writeOn();
Упражнение 19.10
Дана такая иерархия классов:
class Base {
public:
operator int();
operator const char *();
// ...
};
class Derived : public Base {
public:
operator double();
// ...
};
Удастся ли выбрать наилучшую из устоявших функций для каждого из следующих вызовов? Назовите кандидаты, устоявшие функции и преобразования типов аргументов для каждой из них, наилучшую из устоявших (если она есть):
(a) void operate( double );
void operate( string );
void operate( const Base & );
Derived *pd = new Derived;
operate( *pd );
(b) void calc( int );
void calc( double );
void calc( const Derived & );
Base *pb = new Derived;
operate( *pb );
20
20. Библиотека iostream
Частью стандартной библиотеки C++ является библиотека iostream – объектно-ориентированная иерархия классов, где используется и множественное, и виртуальное наследование. В ней реализована поддержка для файлового ввода/вывода данных встроенных типов. Кроме того, разработчики классов могут расширять эту библиотеку для чтения и записи новых типов данных.
Для использования библиотеки iostream в программе необходимо включить заголовочный файл
#include
Операции ввода/вывода выполняются с помощью классов istream (потоковый ввод) и ostream (потоковый вывод). Третий класс, iostream, является производным от них и поддерживает двунаправленный ввод/вывод. Для удобства в библиотеке определены три стандартных объекта-потока:
cin – объект класса istream, соответствующий стандартному вводу. В общем случае он позволяет читать данные с терминала пользователя;
cout – объект класса ostream, соответствующий стандартному выводу. В общем случае он позволяет выводить данные на терминал пользователя;
cerr – объект класса ostream, соответствующий стандартному выводу для ошибок. В этот поток мы направляем сообщения об ошибках программы.
Вывод осуществляется, как правило, с помощью перегруженного оператора сдвига влево (<<), а ввод – с помощью оператора сдвига вправо (>>):
#include
#include
int main()
{
string in_string;
// вывести литерал на терминал пользователя
cout << "Введите свое имя, пожалуйста: ";
// прочитать ответ пользователя в in_string
cin >> in_string;
if ( in_string.empty() )
// вывести сообщение об ошибке на терминал пользователя
cerr << "ошибка: введенная строка пуста!\n";
else cout << "Привет, " << in_string << "!\n";
}
Назначение операторов легче запомнить, если считать, что каждый “указывает” в сторону перемещения данных. Например,
>> x
перемещает данные в x, а
<< x
перемещает данные из x. (В разделе 20.1 мы покажем, как библиотека iostream поддерживает ввод данных, а в разделе 20.5 – как расширить ее для ввода данных новых типов. Аналогично раздел 20.2 посвящен поддержке вывода, а раздел 20.4 – расширению для вывода данных определенных пользователем типов.)
Помимо чтения с терминала и записи на него, библиотека iostream поддерживает чтение и запись в файлы. Для этого предназначены следующие классы:
ifstream, производный от istream, связывает ввод программы с файлом;
ofstream, производный от ostream, связывает вывод программы с файлом;
fstream, производный от iostream, связывает как ввод, так и вывод программы с файлом.
Чтобы использовать часть библиотеки iostream, связанную с файловым вводом/выводом, необходимо включить в программу заголовочный файл
#include
(Файл fstream уже включает iostream, так что включать оба файла необязательно.) Файловый ввод/вывод поддерживается теми же операторами:
#include
#include
#include
#include
int main()
{
string ifile;
cout << "Введите имя файла для сортировки: ";
cin >> ifile;
// сконструировать объект класса ifstream для ввода из файла
ifstream infile( ifile.c_str() );
if ( ! infile ) {
cerr << "ошибка: не могу открыть входной файл: "
<< ifile << endl;
return -1;
}
string ofile = ifile + ".sort";
// сконструировать объект класса ofstream для вывода в файл
ofstream outfile( ofile.c_str() );
if ( ! outfile) {
cerr << "ошибка: не могу открыть выходной файл: "
<< ofile << endl;
return -2;
}
string buffer;
vector< string, allocator > text;
int cnt = 1;
while ( infile >> buffer ) {
text.push_back( buffer );
cout << buffer << (cnt++ % 8 ? " " : "\n" );
}
sort( text.begin(), text.end() );
// выводим отсортированное множество слов в файл
vector< string >::iterator iter = text.begin();
for ( cnt = 1; iter != text.end(); ++iter, ++cnt )
outfile << *iter
<< (cnt % 8 ? " " : "\n" );
return 0;
}
Вот пример сеанса работы с этой программой. Нас просят ввести файл для сортировки. Мы набираем alice_emma (набранные на клавиатуре символы напечатаны полужирным шрифтом). Затем программа направляет на стандартный вывод все, что прочитала из файла:
Введите имя файла для сортировки: alice_emma
Alice Emma has long flowing red hair. Her
Daddy says when the wind blows through her
hair, it looks almost alive, like a fiery
bird in flight. A beautiful fiery bird, he
tells her, magical but untamed. "Daddy, shush, there
is no such creature," she tells him, at
the same time wanting him to tell her
more. Shyly, she asks, "I mean, Daddy, is
there?"
Далее программа выводит в файл outfile отсортированную последовательность строк. Конечно, на порядок слов влияют знаки препинания; в следующем разделе мы это исправим:
"Daddy, "I A Alice Daddy Daddy, Emma Her
Shyly, a alive, almost asks, at beautiful bird
bird, blows but creature," fiery fiery flight. flowing
hair, hair. has he her her her, him
him, in is is it like long looks
magical mean, more. no red same says she
she shush, such tell tells tells the the
there there?" through time to untamed. wanting when
wind
(В разделе 20.6 мы познакомимся с файловым вводом/выводом более подробно.)
Библиотека iostream поддерживает также ввод/вывод в область памяти, при этом поток связывается со строкой в памяти программы. С помощью потоковых операторов ввода/вывода мы можем записывать данные в эту строку и читать их оттуда. Объект для строкового ввода/вывода определяется как экземпляр одного из следующих классов:
istringstream, производный от istream, читает из строки;
ostringstream, производный от ostream, пишет в строку;
stringstream, производный от iostream, выполняет как чтение, так и запись.
Для использования любого из этих классов в программу нужно включить заголовочный файл
#include
(Файл sstream уже включает iostream, так что включать оба файла необязательно.) В следующем фрагменте объект класса ostringstream используется для форматирования сообщения об ошибке, которое возвращается вызывающей программе.
#include
string program_name( "our_program" );
string version( 0.01 );
// ...
string mumble( int *array, int size )
{
if ( ! array ) {
ostringstream out_message;
out_message << "ошибка: "
<< program_name << "--" << version
<< ": " << __FILE__ << ": " << __LINE__
<< " -- указатель равен 0; "
<< " а должен адресовать массив.\n";
// возвращаем строку, в которой находится сообщение
return out_message.str();
}
// ...
}
(В разделе 20.8 мы познакомимся со строковым вводом/выводом более подробно.)
Потоки ввода/вывода поддерживают два предопределенных типа: char и wchar_t. В этой главе мы расскажем только о чтении и записи в потоки данных типа char. Помимо них, в библиотеке iostream имеется набор классов и объектов для работы с типом wchar_t. Они отличаются от соответствующих классов, использующих тип char, наличием префикса ‘w’. Так, объект стандартного ввода называется wcin, стандартного вывода – wcout, стандартного вывода для ошибок – wcerr. Но набор заголовочных файлов для char и wchar_t один и тот же.
Классы для ввода/вывода данных типа wchar_t называются wostream, wistream, wiostream, для файлового ввода/вывода – wofstream, wifstream, wfstream, а для строкового – wostringstream, wistringstream, wstringstream.
20.1. Оператор вывода <<
Оператор вывода обычно применяется для записи на стандартный вывод cout. Например, программа
#include
int main()
{
cout << "сплетница Анна Ливия\n";
}
печатает на терминале строку:
сплетница Анна Ливия
Имеются операторы, принимающие аргументы любого встроенного типа данных, включая const char*, а также типов string и complex из стандартной библиотеки. Любое выражение, включая вызов функции, может быть аргументом оператора вывода при условии, что результатом его вычисления будет тип, принимаемый каким-либо вариантом этого оператора. Например, программа
#include
#include
int main()
{
cout << "Длина 'Улисс' равна:\t";
cout << strlen( "Улисс" );
cout << '\n';
cout << "Размер 'Улисс' равен:\t";
cout << sizeof( "Улисс" );
cout << endl;
}
выводит на терминал следующее:
Длина 'Улисс' равна:7
Размер 'Улисс' равен:8
endl – это манипулятор вывода, который вставляет в выходной поток символ перехода на новую строку, а затем сбрасывает буфер объекта ostream. (С буферизацией мы познакомимся в разделе 20.9.)
Операторы вывода, как правило, удобнее сцеплять в одну инструкцию. Например, предыдущую программу можно записать таким образом:
#include
#include
int main()
{
// операторы вывода можно сцеплять
cout << "Длина 'Улисс' равна:\t";
<< strlen( "Улисс" ) << '\n';
cout << "Размер 'Улисс' равен:\t"
<< sizeof( "Улисс" ) << endl;
}
Сцепление операторов вывода (и ввода тоже) возможно потому, что результатом выражения
cout << "некоторая строка";
служит левый операнд оператора вывода, т.е. сам объект cout. Затем этот же объект передается следующему оператору и далее по цепочке (мы говорим, что оператор << левоассоциативен).
Имеется также предопределенный оператор вывода для указательных типов, который печатает адрес объекта. По умолчанию адреса отображаются в шестнадцатеричном виде. Например, программа
#include
int main()
{
int i = 1024;
int *pi = &i;
cout << "i: " << i
<< "\t&i:\t" << &i << '\n';
cout << "*pi: " << *pi
<< "\tpi:\t" << pi << endl
<< "\t\t&pi:\t" << &pi << endl;
}
выводит на терминал следующее:
i: 1024 &i: 0x7fff0b4
*pi: 1024 pi: 0x7fff0b4
&pi: 0x7fff0b0
Позже мы покажем, как напечатать адреса в десятичном виде.
Следующая программа ведет себя странно. Мы хотим напечатать адрес, хранящийся в переменной pstr:
#include
const char *str = "vermeer";
int main()
{
const char *pstr = str;