五分钟c++(8)iostream 标准库

原书这一章写得太拉垮,我实在看不下去,所以我决定重写一版。

这个库值得拿出来单独写篇文章,是因为这个基本上是每个学c++的人最先接触的标准库。每一个hello world程序里都要用一个cout。在学c++的初期,以及程序不工作的时候我们都想快速的加个debug语句,用iostream基本上都是必不可少的。

我相信大家都知道怎么用cin和cout,可我的问题来了:你能不能解释清楚语句

cout << "hello world!" << endl; 是怎么工作的?

这个语句封装的非常好,所以对于刚开始学c++的人来说,搞清楚这个并不是那么容易。让我来一点一点把这个内容讲清楚。

基本结构

我们先来看看iostream这整个库里有些什么抽象类,整个库的结构大概如下:
在这里插入图片描述
这里我对每个类进行简单的解释:

  • base_ios:这是整个 IOStream 类的最顶层基类,负责处理流的底层细节,比如错误状态标志(good, fail, bad, eof)和格式化标志。ios 从它继承,但通常我们不直接使用它。
  • ios:继承自 base_ios,它管理流的格式化信息和状态。你可以把它看作是所有输入输出流的共同“父亲”。它不直接处理读写操作,而是提供一个通用的框架。
  • istream:这是一个抽象的输入流基类。它定义了所有输入操作的行为,比如用于读取数据的 operator>>get()getline()cin 就是这个类的实例,它连接到标准输入设备(通常是键盘)。
  • ostream:这是一个抽象的输出流基类。它定义了所有输出操作的行为,比如用于写入数据的 operator<<put()cout 就是这个类的实例,它连接到标准输出设备(通常是屏幕)。
  • iostream:继承自 istreamostream。它同时具备输入和输出的能力,所以可以进行双向读写。你可以把 iostream 理解为一个同时能读能写的接口。

非常注意这里的 iostream#include <iostream> 不是一个东西。是一个头文件,里边包含了这个库所有的类,而上述的iostream是一个有双向读写功能的抽象类。

在这些抽象类的基础上,我们有了平时常用的 cin , cout, fstream , sstream 。如下图:
在这里插入图片描述

这里我们可以看到:

cin 是一个 istream ,所以它只有输入功能

cout 是一个 ostream , 所以它只有输出功能

fstreamsstream 都是 iostream , 所以他们既可以输入,也可以输出。

工作原理

整个库的核心思想:流 (Stream)

是一个抽象概念,它代表一个字节序列。你可以从流中读取数据(输入流),也可以向流中写入数据(输出流)。

这个抽象的好处在于,你的代码不需要关心数据的具体来源或目的地。无论是从键盘读取、向屏幕打印,还是读写文件,你都使用同样的操作符 >>(提取)和 <<(插入),这让代码变得非常统一和优雅。

在这一整套的实现中,istream和ostream干了一件超级厉害的事,叫做运算符重载(operator overloading),他们把位移运算符>><< 改写了功能,然后返回了 *this ,这个操作类似于设计模式里的builder pattern,这样可以把所有的操作全部串起来。

还有一点需要解释,cincout 是两个提前创建好的全局对象(global object)所以我们在调用的时候并没有重新建一个新的实例,而是直接用。

cin ,cout 类似,我们还有 cerrclog,这些都是提前定义好的全局对象,所以他们被统称为标准流对象 (Standard Stream Objects)

解释开始的问题

现在我们就可以很容易的解释我刚开始提出的问题:cout << "hello world!" << endl; 是怎么工作的?

首先,我们先调用了全局变量cout

然后用了它在 ostream 里重载的 << ,这个东西把后边的字符串"hello world!" 传到cout 里,在这个类里把他显示到了屏幕上,之后继续返回同样一个cout 实例,

然后对endl 进行同样的操作。

实例

解释清楚了基本概念,我来给几个常用流的例子

标准流对象

需要包含 <iostream> 头文件。

  • std::cin: Console Input。标准输入流,通常关联到键盘。
  • std::cout: Console Output。标准输出流,通常关联到屏幕。它是有缓冲的,意味着数据可能不会立即显示,而是等缓冲区满了或遇到特定条件(如 std::endl)再输出。
  • std::cerr: Console Error。标准错误流,通常也关联到屏幕。它没有缓冲,意味着你写入的数据会立即显示。主要用于输出错误信息。
  • std::clog: Console Log。标准日志流,也用于输出错误信息。与 cerr 不同,clog 有缓冲
#include <iostream>
#include <string>

int main() {
    std::string name;
    int age;

    std::cout << "你好,请输入你的名字:";
    std::cin >> name; // 从键盘读取一个单词到 name

    std::cout << "请输入你的年龄:";
    std::cin >> age; // 从键盘读取一个整数到 age

    std::cout << "欢迎, " << name << "!你 " << age << " 岁了。" << std::endl;

    // 假设发生了一个错误
    std::cerr << "这是一个错误信息!" << std::endl;

    return 0;
}

文件流 (File Streams) - <fstream>

需要包含 <fstream> 头文件。

  • std::ifstream: Input File Stream,用于从文件中读取数据。
  • std::ofstream: Output File Stream,用于向文件中写入数据。
  • std::fstream: File Stream,既可以读文件,也可以写文件。

示例代码:

#include <iostream>
#include <fstream>
#include <string>

int main() {
    // 写入文件
    std::ofstream outFile("user.txt");
    if (outFile.is_open()) {
        outFile << "Alice\n";
        outFile << 30 << std::endl;
        outFile.close(); // 关闭文件
        std::cout << "数据已写入 user.txt" << std::endl;
    }

    // 从文件读取
    std::ifstream inFile("user.txt");
    if (inFile.is_open()) {
        std::string name;
        int age;
        inFile >> name >> age; // 像使用 cin 一样读取
        std::cout << "从文件中读到: " << "姓名=" << name << ", 年龄=" << age << std::endl;
        inFile.close();
    }

    return 0;
}

b. 字符串流 (String Streams) - <sstream>

需要包含 <sstream> 头文件。这是一种在内存中操作的流,它把一个 std::string 对象当作流来处理。非常适合用于数据格式转换。

  • std::istringstream: Input String Stream,从一个 string 中读取数据。
  • std::ostringstream: Output String Stream,向一个 string 中写入数据。
  • std::stringstream: String Stream,既可读也可写。

示例代码:

#include <iostream>
#include <sstream>
#include <string>

int main() {
    // 场景1: 将多种数据类型拼接成一个字符串
    std::ostringstream oss;
    std::string name = "Bob";
    int age = 25;
    double score = 95.5;

    oss << "姓名: " << name << ", 年龄: " << age << ", 分数: " << score;
    std::string result = oss.str(); // 从 ostringstream 获取拼接好的字符串
    std::cout << result << std::endl; // 输出: 姓名: Bob, 年龄: 25, 分数: 95.5

    // 场景2: 从一个字符串中解析数据
    std::string data = "Charlie 40 88.0";
    std::istringstream iss(data);
    
    std::string parsed_name;
    int parsed_age;
    double parsed_score;
    
    iss >> parsed_name >> parsed_age >> parsed_score; // 像使用 cin 一样解析
    std::cout << "解析结果: " << parsed_name << " " << parsed_age << " " << parsed_score << std::endl;

    return 0;
}

这个库里还有格式化标志 (Formatting flags)和流操作符(stream manipulators),我会在之后发布的文章里介绍他们。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值