Ввод-вывод с помощью объектов cin и cout

Практический курс по C/C++: https://stepik.org/course/193691

Раз уж мы в первой программе на С++ использовали объект cout для вывода строки в стандартный выходной поток, то давайте сразу разберем работу двух таких объектов:

  • cin – объект класса istream для работы с потоком stdin;
  • cout – объект класса ostream для работы с потоком stdout.

Если вы не знакомы с концепцией классов и их объектов, то есть, с ООП языка С++, то фраза «объект класса» вас может поставить в тупик. Конечно, мы сейчас не будем пускаться во все тяжкие и уходить в изучение ООП. На данном этапе будет вполне достаточным воспринимать объекты cin/cout, как некие переменные, через которые можно работать со стандартными потоками ввода/вывода.

Я напомню, что в курсе по языку Си мы подробно с вами говорили об этих потоках, которые часто обозначаются через определения stdin/stdout. И я приводил следующую схему их работы:

Так вот, в С++ вместо функций printf(), scanf() и им подобных предлагается использовать более дружественные объекты cout и cin. В чем их дружественность и отличия от уже знакомых нам функций ввода/вывода? Начнем с более простого объекта cout. Чтобы им воспользоваться, в программе на С++ вначале необходимо подключить файл iostream. После этого в пространстве имен std будет доступен объект cout. Затем, объявим в программе несколько переменных разных типов и выведем их в стандартный поток с помощью cout. Сделать это можно следующим образом:

#include <iostream>
 
int main()
{
    char str[100] = "Hi, Sergey!";
    short old = 99;
    double weight = 82.54;
 
    std::cout << str << "\n";
    std::cout << old << "\n";
    std::cout << weight << std::endl;
}

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

printf("%s\n%d\n%.2f\n", str, old, weight);

При использовании объекта cout ничего этого делать не нужно. Просто прописываем переменную, значение которой хотим вывести в поток stdout и все.

Если кому интересно, как это все работает «под капотом», то скажу буквально пару слов. У каждого класса в С++ можно делать переопределение стандартных операций. В частности, операция << для класса ostream переопределена для каждого стандартного, базового типа и компилятор в соответствии с типом переменной (или, в общем случае, по данным в правом операнде), выбирает соответствующую реализацию операции <<. Сама же операция << выполняет передачу в выходной поток stdout соответствующих данных и возвращает объект cout. А раз так, то мы можем записать вывод трех переменных следующим образом:

std::cout << str << '\n' << old << "\n" << weight << std::endl;

Получим абсолютно тот же самый результат.

Второе, на что следует обратить внимание – это использование управляющих символов ‘\n’. Сначала я его прописал, как символ, а второй раз – как строку. Для объекта cout это не имеет большого значения, но, конечно, если нам нужно вывести отдельный символ, то лучше это делать через одинарные кавычки.

В самом конце вывода прописана функция endl. Она выполняет два действия: переводит курсор на новую строку и очищает выходной буфер, указывая конечному устройству, связанному с выходным потоком, что все данные переданы и их можно обрабатывать. В случае с выводом на монитор очистка мало что дает, т.к. информация сразу отображается на экране. Но, вполне могут быть и другие ситуации, когда вместо монитора подключено другое устройство и вывод осуществляется только в момент очистки выходного буфера.

Следующий объект cin работает похожим образом, но осуществляет уже считывание информации из стандартного входного потока stdin и запись прочитанных данных в указанную переменную или, в общем случае, lvalue выражение.

Давайте, для примера выполним считывание данных в переменные old и weight с помощью cin следующим образом:

#include <iostream>
 
int main()
{
    char str[100] = "Hi, Sergey!";
    short old = 99;
    double weight = 82.54;
 
    std::cin >> old;
    std::cin >> weight;
    
    std::cout << str << '\n' << old << '\n' << weight << std::endl;
}

Обратите внимание, что для объекта cin переопределена операция >> также для каждого стандартного типа языка С++. Поэтому нам достаточно прописать переменную без указания ее типа. Причем после имени переменной не нужно прописывать операцию взятия адреса, как это мы делали в функции scanf(). В операции >> для этого применяется механизм ссылок. О них мы с вами еще будем подробнее говорить.

Если данные введены верно (целочисленное значение и вещественное), то переменные old и weight будут содержать корректные введенные значения. Причем ввести их можно каждый с новой строки или через пробел. Если во входном потоке stdin встречаются данные, не соответствующие типу переменной, то значение переменной обнуляется с неверной интерпретацией последующих операций считывания. Поэтому формат входных данных в stdin должен совпадать с типами читаемых данных.

Операция >> после чтения порции данных возвращает объект cin, поэтому два последовательных вызова cin можно объединить в один:

std::cin >> old >> weight;

Результат будет тем же самым.

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

std::cin >> str;

и вводим строку «hello world». В результате в массив str будет помещен только первый фрагмент до пробела, то есть, «hello», а второй останется во входном потоке. То есть, cin со строками работает так же, как и функция scanf() языка Си. Если вам нужно читать строку целиком (до символа перевода строки или до конца данных), то следует использовать новую функцию getline() языка С++. Она работает с объектом std::string и речь о ней еще впереди.

Я думаю, вы теперь в целом хорошо понимаете, как использовать объекты cout/cin языка С++ для работы со стандартными потоками ввода/вывода.

Практический курс по C/C++: https://stepik.org/course/193691

Видео по теме