Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции C++.doc
Скачиваний:
3
Добавлен:
01.05.2025
Размер:
1.44 Mб
Скачать

7.1.1 Потоки

Системы ввода/вывода С и C++ имеют одну общую особенность: они обе оперируют потоками. То, что потоки в С и C++ подобны между собой, означает, что все, что известно о потоках в языке С, полностью применимо и в C++.

7.1.2 Предопределенные потоки C++

Как и в языке С, в C++ существует несколько предопределенных потоков, открывающихся автома­тически вместе с началом выполнения программы. Ими служат cin, cout, cerr и clog. Как извест­но, cin является потоком, ассоциированным со стандартным вводом, a cout представляет собой поток, ассоциированный со стандартным выводом. Потоки cerr и clog используются для вывода сообщений об ошибках. Разница между cerr и clog заключается в том, что, хотя они оба привяза­ны к стандартному выводу, cerr не буферизирован, поэтому все посланные в него данные выво­дятся немедленно. В противоположность этому clog буферизирован, так что данные выводятся только тогда, когда буфер оказывается полным.

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

7.2 Классы потоков C++

C++ обеспечивает поддержку системы ввода/вывода в заголовочном файле iostream.h. В этом файле определены две иерархии классов, поддерживающие операции ввода/вывода. Классом нижнего уровня является streambuf. Этот класс обеспечивает базовые операции ввода/вывода. До тех пор, пока не вводятся свои собственные классы ввода/вывода, непосредственно streambuf не использу­ется. Вторая иерархия классов начинается с класса ios. Он обеспечивает поддержку форматиро­ванного ввода/вывода. От него порождены классы istream, ostream и iostream. Эти классы ис­пользованы для создания потоков, способных осуществлять ввод, вывод и ввод/вывод соответственно. Как будет показано далее, от класса ios порождено много других классов, поддерживающих файлы на диске и форматирование в памяти.

7.3 Создание собственных операторов вставки и извлечения

В предыдущих главах создаваемые функции-члены классов осуществляли вывод и ввод данных класса с помощью вызова функций наподобие show_data() или get_data(). Хотя технически здесь нет ничего неправильного, язык C++ предоставляет гораздо более совершенный способ выполне­ния операций ввода/вывода классов с помощью перегрузки операторов << и >>.

На языке C++ оператор << называют оператором вставки (insertion), потому что он вставляет символы в поток. Аналогичным образом оператор >> называется оператором извлечения (extraction), поскольку он извлекает символы из потока. Операторы, перегружающие эти операторы вставки и извлечения, обычно называют инсертером (inserter) и экстрактором (extractor) соответственно. Базовые операторы вставки и извлечения перегружаются в файле iostream.h для того, чтобы выполнять потоковый ввод/вывод любых встроенных типов C++. В этом разделе объясняется, каким образом определить эти операторы по отношению к собственным классам.

7.3.1 Создание операторов вставки

В C++ имеется легкий способ определения оператора вставки для создаваемых классов. В следую­щем простом примере создается оператор вставки для ранее уже встречавшегося класса point:

class point

{

public:

int x, у, z;

point(int a, int b, int с) { x=a; y=b; z=c; }

}

Для того, чтобы определить оператор вставки для объекта класса point, необходимо перегрузить оператор << по отношению к этому классу. Один из способов показан ниже.

// вывод координат x, y, z (оператор вставки для point)

ostream& operator<<(ostream &stream, point &obj)

{

stream << obj.x << ", " << obj.y << ", " << obj.z << "\n";

return stream; // возврат потока

}

Многие особенности этой функции являются общими для всех функций вставки. Прежде всего, обратим внимание, что в качестве возвращаемого значения этой функции объявлен объект типа ostream. Это необходимо для того, чтобы один оператор мог содержать несколько операторов вставки. Далее, функция имеет два параметра. Первым служит ссылка на поток, который фигури­рует в левой части оператора <<. Вторым параметром служит объект с правой стороны операто­ра <<. В самой функции осуществляется вывод трех величин, содержащихся в объекте point, после чего возвращается поток stream. Следующая короткая программа служит для демонстра­ции оператора вставки:

int main()

{

point a(1, 2, 3), b(3, 4, 5), c(5, 6, 7);

cout << a << b << c;

return 0;

}

Общая форма функции вставки показана ниже:

ostream& operator<<(ostream &поток, тип_класса &объект)

{

// характерный для типа код

return stream; // возврат потока

}

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

В программе перегруженная функция вставки не является членом класса point. Действительно, ни функция вставки, ни функция извлечения не могут быть членами клас­са. Причина заключается в том, что если функция-оператор является членом класса, то левым операндом, неявно передаваемым с использованием указателя this, служит объект того класса, который осуществляет вызов функции-оператора. И нет способа изменить такой порядок. Вместе с тем при перегрузке оператора вставки левым аргументом является поток, а правым аргументом — объект. Поэтому перегруженные операторы вставки не могут быть функциями-членами. Этот факт вызывает логичный вопрос: как перегруженный оператор вставки может получить доступ к частным членам класса? В предыдущей программе переменные x, y и z были публичны­ми, так что оператор вставки имел к ним доступ. Однако защита данных является одной из важ­нейших особенностей объектно-ориентированного программирования, и требовать, чтобы все данные были публичными, значит противоречить самому духу ООП. Тем не менее, данная проблема имеет решение: оператор вставки может быть другом класса. В качестве друга класса, для которого он определен, он имеет доступ к частным данным. Иллюстрация этого приводится в следующем примере:

#include <iostream.h>

class point

{

int x, y, z; // теперь частные

public:

point(int a, int b, int c) { x=a; y=b; z=c; }

friend ostream& operator<<(ostream &stream, point &obj);

};

ostream& operator<<(ostream &stream, point &obj)

{

stream << obj.x << ", " << obj.y << ", " << obj.z << "\n";

return stream; // возврат потока

}

int main()

{

point a(1, 2, 3), b(3, 4, 5), c(5, 6, 7);

cout << a << b << c;

return 0;

}

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