УДК 004.42

О ПОТОКАХ ВВОДА-ВЫВОДА В С++: НЕКОТОРЫЕ ВОЗМОЖНОСТИ КЛАССА OSTREAM

Дмитриев Владислав Леонидович1, Зайцева Нина Леонидовна1
1Стерлитамакский филиал Башкирского государственного университета

Аннотация
Обсуждение вопросов ввода-вывода в языке программирования С++ представляет собой определенную проблему. Дело в том, что практически любая программа использует ввод и вывод, но средства ввода-вывода в С++ традиционно разрабатывались лишь для работы с несколькими встроенными типами данных. С другой стороны, С++ не имеет операций ввода-вывода, встроенных в сам язык. В связи с этим в работе обсуждаются некоторые возможности, предоставляемые классом ostream, с которыми будет полезно ознакомить учащихся в процессе обучения программированию.

Ключевые слова: класс ostream, потоки ввода-вывода, язык программирования С++


ABOUT STREAMS OF INPUT/OUTPUT IN C++: SOME OF THE CAPABILITY OF THE OSTREAM CLASS

Dmitriev Vladislav Leonidovich1, Zaytseva Nina Leonidovna1
1Sterlitamak branch of the Bashkir state University

Abstract
Discussion the questions input/output in C++ represents is a some problem. The fact is that practically every program uses input and output, but means input/output in C++ have traditionally been designed only to work with several built-in data types. On the other hand, C++ has no input/output built into the language itself. In this regard, the paper discusses some of the opportunities provided by the ostream class, which will be helpful for introduce students to the process of teaching programming.

Keywords: input/output streams, the C++programming language, the ostream class


Рубрика: 05.00.00 ТЕХНИЧЕСКИЕ НАУКИ

Библиографическая ссылка на статью:
Дмитриев В.Л., Зайцева Н.Л. О потоках ввода-вывода в С++: некоторые возможности класса ostream // Современные научные исследования и инновации. 2016. № 12 [Электронный ресурс]. URL: http://web.snauka.ru/issues/2016/12/75884 (дата обращения: 30.04.2017).

Область применения языка программирования C++ в настоящее время весьма обширна, он широко используется для разработки программного обеспечения, и является одним из самых популярных языков программирования. В настоящее время существует множество реализаций языка C++, как коммерческих, так и бесплатных (например, Microsoft Visual C++ 2005-2013).

При обучении программированию на С++ необходимо уделять достаточно внимания работе с потоками ввода-вывода [1], так как практически любая программа использует ввод и вывод. Однако почти всегда изучение этой темы ограничивается лишь стандартным использованием операций ввода и вывода на основе cin и cout. Другие особенности и возможности потоков остаются так и не затронутыми, и неизвестными для учащихся (обычно в дальнейшем особо заинтересованные учащиеся изучают их самостоятельно).

В данной статье рассматриваются некоторые вопросы, связанные с перегруженной операцией << и отдельными методами класса ostream. Предполагается, что учащиеся уже имеют базовые представления о языке программирования C++.

Вывод в С++ рассматривается как поток байтов, и на самом деле представляет собой преобразование объектов некоторого типа в последовательность символов. Это связано с тем, что многие виды данных (даже стандартных типов) организованы в более крупные единицы, чем байт. Поэтому наиболее важной задачей, стоящей перед классом ostream, является задача преобразования типов, таких как int, float, double и других, в поток символов. Для выполнения таких преобразований в классе ostream предусмотрено несколько методов [1, 4-6].

Как понимать утверждение “объект представляет собой поток”? Например, когда в файле iostream объявляется объект cout для программы, то этот объект получает данные-члены, содержащие информацию относительно вывода (ширина поля вывода, количество знаков после десятичной точки, адрес объекта streambuf, и др.)

Рассмотрим, что происходит, когда в программе встречается инструкция cout [4]:

cout<<”Введите строку данных”;

Данная инструкция помещает символы из строки “Введите строку данных” в буфер, управляемый cout через объект streambuf. Буфер (buffer) – это блок оперативной памяти, выполняющий роль посредника, который используется средствами ввода-вывода для промежуточного (временного) хранения данных, передаваемых между программой и устройством хранения.

Буфер помогает, например, согласовать обмен данными между программами и приводами дисков: программы очень часто обрабатывают данные по одному байту за раз, а приводы дисков обычно передают информацию блоками размером по 512 байт и более. Кроме того, если считывать данные из файла по одному символу, то такое чтение требует больших аппаратных затрат, и достаточно медленно. Буферизованный подход заключается в том, что достаточно большая порция данных читается с диска и сохраняется в буфере. Затем можно производить чтение данных из буфера по одному символу – это будет намного быстрее, так как в таком случае производится чтение байтов из памяти. После того, как программа в процессе чтения и обработки данных достигнет конца буфера, она может считать следующую порцию данных с диска. Аналогично, при выводе программа может сначала наполнить буфер данными, а затем целиком передать этот блок данных на жесткий диск; затем буфер используется для приема новых данных.

При вводе данных с клавиатуры программа, в принципе, не нуждается в буфере, так как клавиатурный ввод даёт один символ за раз. Однако буферизированный ввод с клавиатуры обеспечивает возможность исправить введенные данные до того, как они поступят в программу. Программа на С++ обычно сбрасывает содержимое такого буфера в программу после нажатия на клавишу “Enter“. При выводе на экран буфер обычно сбрасывается при встрече символа новой строки.

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

Класс ostream определяет оператор << (“записать в”) для управления выводом встроенных типов (иногда операцию << называют операцией вставки). По умолчанию операция << используется также в качестве битовой операции сдвига, однако это не имеет отношения к выводу: класс ostream переопределяет операцию << для организации вывода, причем эта операция перегружена и может применяться со всеми базовыми типами C++. Класс ostream предлагает определение функции operator<<() для каждого из этих типов данных, причем эта функция возвращает ссылку на объект ostream, для которого она вызывалась. Именно поэтому к ней можно применить другой operator<<() и может быть организован сцепленный вывод данных:

cout<<”Значение элемента a["<<i<<"] равно “<<a[i];

По той же причине строка кода

cout<<”a = “<<a<<endl;

будет интерпретироваться (не для компилятора Borland C++ 5.02) как

operator<< (cout, “a = “).operator<< (a)<<endl;

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

Кроме функций operator<<(), класс ostream предоставляет еще и следующие методы: метод put() для отображения символов и метод write() для отображения строк. Методы реализованы в виде шаблонов, а вызываются с применением обычной нотации вызова метода класса:

cout.put(‘P’);

Функция-член put() класса ostream возвращает ссылку на вызывающий объект, поэтому можно использовать сцепленный вывод:

cout.put(‘P’).put(‘u’).put(‘t’)<<endl;

Фактически вызов функции cout.put(‘P’) возвращает cout, который, в свою очередь, снова вызывает метод put().

Так как метод put() выводит символ, а сам метод реализован в виде шаблона, то можно применять put() и с аргументами числовых типов. При этом будет выведен символ с соответствующим числовым ASCII-кодом:

cout.put(97); // a

cout.put(117.234); // u

Некоторые старые компиляторы ошибочно перегружали метод put() для типов аргументов char, unsigned char и signed char, что в результате приводило к неоднозначности. Поэтому в таких случаях было необходимо указывать явное преобразование типов. Например, чтобы предыдущий код работал в компиляторе Borland C++ 5.02, его нужно видоизменить, включив явное преобразование типов:

cout.put((char)97); // a

cout.put((char)117.234); // u

Рассмотрим теперь метод write(). Он имеет два аргумента, первый из которых представляет собой адрес строки, которую необходимо отобразить, а второй – указывает, сколько символов нужно отобразить. Тип возвращаемого функцией write() значения есть ostream &, поэтому вызов cout.write() возвращает объект cout.

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

Ниже приведен фрагмент кода, в котором показано использование метода write().

const char *s1 = “Москва”;

const char *s2 = “Стерлитамак”;

int n=strlen(s1);

for(int i=1; i<=n; cout.write(s1,i++)<<endl);

cout.write(s1,30)<<endl;

В результате работы цикла for этого фрагмента на экран будут выданы следующие строки:

М

Мо

Мос

Моск

Москв

Москва

Результат работы следующей после цикла for строки будет зависеть от используемого компилятора. Так, для компиляторов Borland C++ 5.02 и wxDev-C++ 7.4, скорее всего, будет выдана строка “Москва”, после которой сразу же – строка “Стерлитамак” и еще часть символов (до 30). Таким образом, отмеченные выше компиляторы располагают строки рядом, в соседних ячейках памяти. Этого нельзя сказать о компиляторе Microsoft Visual C++ 2005 Express, – он чаще всего не располагает строки в соседних ячейках памяти.

Как и метод put(), метод write() можно использовать с числовыми данными. Но в данном случае методу write() нужно передать адрес числа, приведя его тип к char*:

long a1 = 97, a2 = 24929, a3 = 6381921;

cout.write((char*) &a1, sizeof(long))<<endl;

cout.write((char*) &a2, sizeof(long))<<endl;

cout.write((char*) &a3, sizeof(long))<<endl;

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

a

aa

aaa

Полученный результат легко объясняется с точки зрения его интерпретации как ASCII-кода символа в двоичном представлении, что показано ниже в таблице 1. Таким образом, в некоторых случаях имеет смысл использовать метод write() для отображения числовых данных.

Таблица 1. Интерпретация результата работы метода write() для числовых данных типа long

Результат работы
метода write()
Значение числовой
переменной
Двоичный код числовой
переменной
a 97 01100001
aa 24929 01100001 01100001
aaa 6381921 01100001 01100001 01100001

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


Библиографический список
  1. Дмитриев В.Л. Теория и практика программирования на С++. – Стерлитамак: РИО СФ БашГУ, 2013. – 308 с.
  2. Дмитриев В.Л. Тестирование в игровой форме как способ проверки усвоения учебного материала // Информатика в школе. 2012. №10 (83). – С. 41-43.
  3. Прата С. Язык программирования С++. Лекции и упражнения, 5-е изд.: Пер. с англ. – М.: Вильямс, 2007. – 1184 с.
  4. Страуструп Б. Язык программирования С++. Специальное издание. –  М.: Бином, 2004. – 1054 с.
  5. Stroustrup Bjarne. The C++ programming language / Bjarne Stroustrup. – Fourth edition. – Boston: Addison-Wesley, 2013. – 1368 p.
  6. Дмитриев В.Л. Компьютерная программа для проведения тестирования с поддержкой произвольного расположения материалов теста // Информатика и образование. 2014. №2 (251). – С. 74-77.


Все статьи автора «Дмитриев Владислав Леонидович»


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

Связь с автором (комментарии/рецензии к статье)

Оставить комментарий

Вы должны авторизоваться, чтобы оставить комментарий.

Если Вы еще не зарегистрированы на сайте, то Вам необходимо зарегистрироваться:
  • Регистрация