1、istream 和 ostream
1.1、istream类
istream
类用于从标准输入设备(例如键盘)或其他输入设备(如文件)读取数据,它是所有输入流类的基类。
常用的成员函数:
operator>>
:提取运算符,用于从输入流中提取数据。
>> 的实现原理:
在
ostream
类中,<<
操作符被重载为一个函数,该函数能够处理各种类型的输出,比如整型、浮点型、字符串等。每次使用<<
时,数据都会传递给该运算符函数,最终输出到流所指向的设备上(如终端或文件)。典型的输出运算符重载形式为:std::ostream& operator<<(std::ostream& os, int value); std::ostream& operator<<(std::ostream& os, double value); std::ostream& operator<<(std::ostream& os, const char* str);
链式调用的原理:
<<
操作符的返回值是对输出流对象本身的引用(std::ostream&
),这使得链式调用成为可能。如下面例子,std::cout << "x = "
返回的是std::ostream&
,接着x
通过下一个<<
操作符继续输出。std::cout << "x = " << x << ", y = " << y;
get()
:从输入流中获取一个字符或字符数组。
char ch;
std::cin.get(ch); // 读取一个字符
getline()
:从输入流中读取一行字符串。
std::string line;
std::getline(std::cin, line); // 读取一行字符串
1.2、ostream 类
ostream
类用于将数据输出到标准输出设备(例如屏幕)或其他输出设备,它是所有输出流类的基类。
常用的成员函数:
operator<<
:插入运算符,用于将数据输出到流中(实现原理与 >> 类似)。
put()
:输出一个字符。
std::cout.put('A'); // 输出字符 'A'
write()
:输出一系列字符。
char str[] = "C++ Programming";
std::cout.write(str, sizeof(str)); // 输出字符串
2、iostream
iostream
类是 istream
和 ostream
的派生类,能够同时执行输入和输出操作,通常用于文件或其他双向通信的场景。
为什么不直接设计一个 iostream,转而先设计 istream 和 ostream 呢?
- 在设计流类时,将输入和输出职责分开,使每个类的职责更加明确,便于理解和维护。这符合单一职责原则,即每个类应该只负责一件事情。
分离
istream
和ostream
为两个独立的类为以后的扩展提供了更大的灵活性。例如ifstream
类只需要处理文件输入,它只需继承 istream 即可,而ofstream
类只处理文件输出,它继承自ostream
。在某些特殊情况下,程序既需要处理输入也需要处理输出,iostream
就发挥了作用(fstream
同时用于文件读写)。
2.1、标准输入输出对象
在 iostream
库中,已经定义了一些常用的全局流对象:
std::cin
:标准输入流,通常与键盘关联,用于从标准输入读取数据。std::cout
:标准输出流,通常与显示器关联,用于向标准输出设备(屏幕)输出数据。std::cerr
:标准错误输出流,通常与显示器关联,用于输出错误信息,不带缓冲。std::clog
:标准日志输出流,通常用于输出日志信息,带缓冲。
int age;
std::cin >> age; // 从键盘读取一个整数
std::cout << "Hello, World!" << std::endl; // 向屏幕输出 "Hello, World!"
std::cerr << "Error: Invalid input" << std::endl; // 输出错误信息
std::clog << "Log: Application started" << std::endl; // 输出日志信息
std::cin
、std::cout
、std::cerr
和std::clog
的本质:
这四个标准流对象本质上是全局对象。它们是 C++ 标准库中定义的对象,专门用于处理标准输入、标准输出和错误输出的流操作。根据标准库的定义,这些流对象大致是以如下方式声明的(虽然实际的实现可能会有所不同):
namespace std { extern istream cin; // 标准输入 extern ostream cout; // 标准输出 extern ostream cerr; // 标准错误输出(无缓冲) extern ostream clog; // 标准日志输出(带缓冲) }
2.2、格式化输出
通过 iostream
,可以对输出进行格式化。以下是几种常用的格式化方法:
-
std::endl
:输出换行并刷新输出缓冲区。 -
std::setw()
:设置字段宽度,定义在iomanip
头文件中。 -
std::setprecision()
:设置浮点数的精度,定义在iomanip
头文件中。 -
std::fixed
和std::scientific
:设置浮点数的显示格式。
std::cout << "Hello, World!" << std::endl;
std::cout << std::setw(10) << 123; // 输出的数字宽度为10个字符
double pi = 3.14159;
std::cout << std::setprecision(3) << pi; // 输出为 3.14
double num = 1234.56789;
std::cout << std::fixed << num; // 以固定小数点格式输出
std::cout << std::scientific << num; // 以科学计数法格式输出
std::endl
的本质:本质上是一个函数,而不是一个简单的换行符。它的功能不仅仅是插入换行符(
\n
),它还会刷新输出缓冲区,确保缓冲区中的数据被立即输出到设备(如终端、文件等)。std::endl
是定义在ostream
类中的模板函数,它的声明通常如下:namespace std { template <class charT, class traits> basic_ostream<charT, traits>& endl(basic_ostream<charT, traits>& os) { os.put(os.widen('\n')); // 插入换行符 os.flush(); // 刷新缓冲区 return os; } }
2.3、状态标志
在使用 iostream
进行输入输出时,流可能会出现一些错误情况,比如输入格式不正确、文件结束等。C++ 提供了一些函数和状态标志来检查流的状态:
eof()
:检查是否到达文件末尾。
if (std::cin.eof())
{
std::cout << "End of file reached" << std::endl;
}
fail()
:检查上一次输入操作是否失败。
if (std::cin.fail())
{
std::cerr << "Input failed!" << std::endl;
}
bad()
:检查是否发生严重的输入输出错误。
if (std::cin.bad())
{
std::cerr << "A critical error occurred!" << std::endl;
}
clear()
:重置流的状态标志。
std::cin.clear(); // 清除所有错误标志