конца области определенности потока.
использования и закрытие их только после последнего использования.
по-разному решать эту задачу. В конце концов, решение - это
прерогатива реализации, и оно должно быть скрыто от пользователя. Здесь
создания и уничтожения глобальных объектов различных типов.
был включен в раздельно компилировавшиеся программные
файлы:
class Io_init {
static int count;
//...
public:
Io_init();
^Io_init();
};
static Io_init io_init ;
Для каждого программного файла определен свой объект с именем io_init.
Конструктор для объектов io_init использует Io_init::count как первый
признак того, что действительная инициализация глобальных объектов
потоковой библиотеки ввода-вывода сделана в точности один раз:
Io_init::Io_init()
{
if (count++ == 0) {
// инициализировать cout
// инициализировать cerr
// инициализировать cin
// и т.д.
}
}
Обратно, деструктор для объектов io_init использует Io_count, как
последнее указание на то, что все потоки закрыты:
Io_init::^Io_init()
{
if (--count == 0) {
// очистить cout (сброс, и т.д.)
// очистить cerr (сброс, и т.д.)
// очистить cin
// и т.д.
}
}
Это общий прием работы с библиотеками, требующими инициализации и
удаления глобальных объектов. Впервые в С++ его применил Д. Шварц.
В системах, где при выполнении все программы размещаются в основной
памяти, для этого приема нет помех. Если это не так, то накладные
расходы, связанные с вызовом в память каждого программного файла
для выполнения функций инициализации, будут заметны. Как всегда,
лучше, по возможности, избегать глобальных объектов. Для классов,
в которых каждая операция значительна по объему выполняемой работы,
чтобы гарантировать инициализацию, было бы разумно проверять такие
первые признаки (наподобие Io_init::count) при каждой операции.
Однако, для потоков такой подход был бы излишне расточительным.
10.5.2 Строковые потоки
Как было показано, поток может быть привязан к файлу, т.е. массиву
символов, хранящемуся не в основной памяти, а, например, на диске. Точно
так же поток можно привязать к массиву символов в основной памяти.
Например, можно воспользоваться выходным строковым потоком ostrstream
для форматирования сообщений, не подлежащих немедленной печати:
char* p = new char[message_size];
ostrstream ost(p,message_size);
do_something(arguments,ost);
display(p);
С помощью стандартных операций вывода функция do_something может писать
в поток ost, передавать ost подчиняющимся ей функциям и т.п. Контроль
переполнения не нужен, поскольку ost знает свой размер и при заполнении
перейдет в состояние, определяемое fail(). Затем функция display может
послать сообщение в "настоящий" выходной поток. Такой прием наиболее
подходит в тех случаях, когда окончательная операция вывода
предназначена для записи на более сложное устройство, чем традиционное,
ориентированное на последовательность строк, выводное устройство.
Например, текст из ost может быть помещен в фиксированную область на экране.
Аналогично, istrstream является вводным строковым потоком,
читающим из последовательности символов, заканчивающейся нулем:
void word_per_line(char v[], int sz)
/*
печатать "v" размером "sz" по одному слову в строке
*/
{
istrstream ist(v,sz); // создать istream для v
char b2[MAX]; // длиннее самого длинного слова
while (ist>>b2) cout <
}
Завершающий нуль считается концом файла.
Строковые потоки описаны в файле .
10.5.3 Буферизация
Все операции ввода-вывода были определены без всякой связи с типом
файла, но нельзя одинаково работать со всеми устройствами без учета
алгоритма буферизации. Очевидно, что потоку ostream, привязанному к
строке символов, нужен не такой буфер, как ostream, привязанному к
файлу. Такие вопросы решаются созданием во время инициализации разных
буферов для потоков разных типов. Но существует только один набор
операций над этими типами буферов, поэтому в ostream нет функций, код
которых учитывает различие буферов. Однако, функции, следящие за
переполнением и обращением к пустому буферу, являются виртуальными.
Это хороший пример применения виртуальных функций для единообразной
работы с эквивалентными логически, но различно реализованными
структурами, и они вполне справляются с требуемыми алгоритмами буферизации.
Описание буфера потока в файле может выглядеть следующим
образом:
class streambuf { // управление буфером потока
protected:
char* base; // начало буфера
char* pptr; // следующий свободный байт
char* gptr; // следующий заполненный байт
char* eptr; // один из указателей на конец буфера
char alloc; // буфер, размещенный с помощью "new"
//...
// Опустошить буфер:
// Вернуть EOF при ошибке, 0 - удача
virtual int overflow(int c = EOF);
// Заполнить буфер:
// Вернуть EOF в случае ошибки или конца входного потока,
// иначе вернуть очередной символ
virtual int underflow();
//...
public:
streambuf();
streambuf(char* p, int l);
virtual ~streambuf();
int snextc() // получить очередной символ
{
return (++gptr==pptr) ? underflow() : *gptr&0377;
}
int allocate(); // отвести память под буфер
//...
};
Подробности реализации класса streambuf приведены здесь только для
полноты представления. Не предполагается, что есть общедоступные
реализации, использующие именно эти имена. Обратите внимание на
определенные здесь указатели, управляющие буфером; с их помощью
простые посимвольные операции с потоком можно определить максимально
эффективно (и причем однократно) как функции-подстановки. Только
функции overflow() и underflow() требует своей реализации для каждого
алгоритма буферизации, например:
class filebuf : public streambuf {
protected:
int fd; // дескриптор файла
char opened; // признак открытия файла
public:
filebuf() { opened = 0; }
filebuf(int nfd, char* p, int l)
: streambuf(p,l) { /* ... */ }
~filebuf() { close(); }
int overflow(int c=EOF);
int underflow();
filebuf* open(char *name, ios::open_mode om);
int close() { /* ... */ }
//...
};
int filebuf::underflow() // заполнить буфер из "fd"
{
if (!opened || allocate()==EOF) return EOF;
int count = read(fd, base, eptr-base);
if (count < 1) return EOF;
gptr = base;
pptr = base + count;
return *gptr & 0377; // &0377 предотвращает размножение знака
}
За дальнейшими подробностями обратитесь к руководству по реализации
класса streambuf.
Достарыңызбен бөлісу: