C++ 输出流(Output Stream)全解析

一、什么是"输出流"?

输出流(Output Stream) 是 C++ 中用于 将数据从程序发送到外部设备 的抽象机制。

数据像"水流"一样,从程序流向:屏幕、文件、网络、字符串等。


二、输出流家族总览

cpp 复制代码
#include <iostream>     // std::cout, std::cerr, std::clog
#include <fstream>      // std::ofstream
#include <sstream>      // std::ostringstream
#include <iomanip>      // 格式控制
用途 继承关系
std::ostream 通用输出流基类 std::iosstd::ostream
std::ofstream 文件输出流 std::ostream
std::ostringstream 字符串输出流 std::ostream
std::cout 标准输出(屏幕) std::ostream
std::cerr 标准错误(屏幕,不缓冲) std::ostream
std::clog 标准日志(屏幕,有缓冲) std::ostream

三、核心操作:<< 插入运算符

cpp 复制代码
std::cout << "Hello" << 42 << 3.14 << '\n';

工作原理:

  1. 自动类型转换
  2. 链式调用 (返回 *this
  3. 缓冲机制(默认不立即刷新)
cpp 复制代码
std::cout << "Loading" << std::flush;  // 强制刷新
std::cout << "Loading" << std::endl;   // 换行 + 刷新

四、标准输出流对比

用途 缓冲 典型场景
std::cout 正常输出 有缓冲 程序结果
std::cerr 错误信息 无缓冲 调试、错误
std::clog 日志信息 有缓冲 运行日志
cpp 复制代码
std::cout << "Info: Starting...\n";
std::cerr << "ERROR: File not found!\n";
std::clog << "LOG: User logged in\n";

五、输出流的成员函数

函数 功能 示例
operator<< 插入数据 cout << x;
put() 输出单个字符 cout.put('A');
write() 输出原始字节 cout.write(buf, n);
flush() 强制刷新缓冲区 cout.flush();
seekp() 设置写位置(文件流) file.seekp(0);
tellp() 获取当前写位置 auto pos = file.tellp();

六、格式控制(<iomanip>)------ 核心!

操纵符 作用 示例
std::endl 换行 + 刷新 cout << endl;
std::flush 强制刷新 cout << flush;
std::setw(n) 设置字段宽度 cout << setw(10) << x;
std::setfill(c) 填充字符 cout << setfill('0');
std::left / std::right / std::internal 对齐方式 cout << left;
std::hex / std::dec / std::oct 进制 cout << hex << 255;ff
std::showbase 显示进制前缀 0xff, 077
std::boolalpha true/false cout << boolalpha << true;
std::fixed 固定小数点 3.14
std::scientific 科学计数法 3.140000e+00
std::setprecision(n) 精度 setprecision(2)

示例:格式化表格

cpp 复制代码
#include <iostream>
#include <iomanip>
using namespace std;

cout << left << setw(15) << "Name"
     << right << setw(10) << "Age"
     << setw(12) << "Salary" << endl;

cout << setfill('-') << setw(37) << "" << setfill(' ') << endl;

cout << left << setw(15) << "Alice"
     << right << setw(10) << 25
     << setw(12) << fixed << setprecision(2) << 50000.50 << endl;

输出:

复制代码
Name           Age      Salary
-------------------------------------
Alice           25    50000.50

七、输出到文件

cpp 复制代码
std::ofstream file("output.txt");
if (!file.is_open()) {
    std::cerr << "无法打开文件!\n";
    return 1;
}

// 写入
file << "Hello, 文件!" << 42 << '\n';

// 追加模式
std::ofstream file2("log.txt", std::ios::app);
file2 << "新日志\n";

模式标志

模式 含义
std::ios::out 写入(默认)
std::ios::app 追加
std::ios::trunc 清空文件
std::ios::binary 二进制模式

八、字符串输出流:std::ostringstream

cpp 复制代码
#include <sstream>

std::ostringstream oss;
oss << "Score: " << 95 << ", Rank: " << 1;
std::string result = oss.str();  // "Score: 95, Rank: 1"

典型用途:

  • 日志拼接
  • JSON 构造
  • 格式化输出再发送
cpp 复制代码
std::string json = [&]{
    std::ostringstream oss;
    oss << "{ \"name\": \"" << name << "\", \"age\": " << age << " }";
    return oss.str();
}();

九、自定义输出(重载 <<

cpp 复制代码
struct Point {
    int x, y;
};

// 友元函数(推荐)
std::ostream& operator<<(std::ostream& os, const Point& p) {
    os << "(" << p.x << ", " << p.y << ")";
    return os;
}

// 使用
Point p{10, 20};
std::cout << p << "\n";  // (10, 20)

十、底层机制:streambuf

cpp 复制代码
std::cout.rdbuf()                    // 获取缓冲区
std::cout.rdbuf(new_buf)             // 替换缓冲区

应用:重定向输出

cpp 复制代码
// 将 cout 重定向到文件
std::ofstream file("log.txt");
std::streambuf* old_buf = std::cout.rdbuf(file.rdbuf());

// 现在 cout 写入文件
std::cout << "这会写入文件!\n";

// 恢复
std::cout.rdbuf(old_buf);

十一、性能优化技巧

技巧 说明
std::ios::sync_with_stdio(false); 关闭 C 流同步,提速 5~10 倍
cout.tie(nullptr); 解绑 cincout
减少 endl '\n' 替代(endlflush
批量写入 write() 写大块数据
cpp 复制代码
int main() {
    std::ios::sync_with_stdio(false);
    cout.tie(nullptr);

    for (int i = 0; i < 1000000; ++i) {
        cout << i << '\n';  // 比 endl 快得多
    }
}

十二、缓冲机制详解

缓冲类型 触发刷新条件
全缓冲(文件) 缓冲区满
行缓冲cout 遇到 \n
无缓冲cerr 立即输出
cpp 复制代码
std::cout << "A";     // 还在缓冲区
std::cout << "B\n";   // 遇到 \n,刷新 → 输出 "AB"
std::cout << std::flush;  // 强制刷新

十三、异常机制(可选)

cpp 复制代码
std::cout.exceptions(std::ios::failbit | std::ios::badbit);

try {
    std::cout << "写入...";
} catch (const std::ios::failure& e) {
    std::cerr << "写入失败: " << e.what() << "\n";
}

十四、完整示例:日志系统

cpp 复制代码
class Logger {
    std::ostringstream buffer;
    std::ofstream file;

public:
    Logger(const std::string& filename) : file(filename, std::ios::app) {}

    template<typename T>
    Logger& operator<<(const T& data) {
        buffer << data;
        return *this;
    }

    Logger& operator<<(std::ostream& (*manip)(std::ostream&)) {
        if (manip == static_cast<std::ostream& (*)(std::ostream&)>(std::endl)) {
            std::string line = buffer.str();
            std::cout << "[LOG] " << line << std::endl;
            if (file) file << line << std::endl;
            buffer.str("");  // 清空
        }
        return *this;
    }
};

// 使用
Logger log("app.log");
log << "User " << username << " logged in at " << time << endl;

十五、常见错误与防御性编程

错误 后果 正确做法
cout << endl 频繁使用 性能低下 '\n'
文件未关闭 资源泄露 RAII(ofstream 自动关闭)
忘记 flush 输出延迟 关键日志用 cerrflush
忽略返回值 写入失败未检测 检查 if (!file)

十六、总结:输出流核心要点

要点 内容
核心操作 <<(插入)、endlflush
格式控制 setw, setprecision, hex, fixed
三大标准流 cout(正常)、cerr(错误)、clog(日志)
性能优化 sync_with_stdio(false) + '\n'
高级用法 ostringstream 拼接、rdbuf() 重定向
自定义 重载 << 支持结构体

推荐学习资源

  • 《C++ Primer》 第 8 章:IO 库
  • cppreference: std::basic_ostream
  • 《Effective C++》 Item 3:使用 const

一句话总结
输出流是 C++ 与外界沟通的桥梁,掌握 <<iomanipostringstream 就能写出优雅、可读、高效的输出代码。

相关推荐
余道各努力,千里自同风3 小时前
如何使用 Promise.all() 处理异步并发操作?
开发语言·前端·javascript
小白讲编程3 小时前
C++ 基础学习总结:从入门到构建核心认知
c++·学习·青少年编程
国服第二切图仔3 小时前
Rust开发之使用 Trait 定义通用行为——实现形状面积计算系统
开发语言·网络·rust
前端小咸鱼一条4 小时前
14. setState是异步更新
开发语言·前端·javascript
L_09074 小时前
【Algorithm】Day-10
c++·算法·leetcode
15Moonlight4 小时前
09-MySQL内外连接
数据库·c++·mysql
无知就要求知4 小时前
golang封装可扩展的crontab
开发语言·后端·golang
weixin_467209284 小时前
Qt Creator打开项目提示no valid settings file could be found
开发语言·qt
mit6.8244 小时前
归并|线段树|树状数组
c++