乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • C++ 学习路径
  • 第1章 C++概述与开发环境

    • C++ 语言概述与编译模型
    • 第一个 C++ 程序与规范
    • 开发环境配置
    • 预处理指令详解
  • 第2章 基本语法与类型系统

    • 变量与基本类型
    • 类型转换
    • 枚举类型
    • 引用与指针
    • 数组与 stdarray
    • 字符串与原始字符串字面量
    • const 与 constexpr
    • nullptr 与空指针
    • 类型推导 auto 与 decltype
    • 基于范围的 for 循环
    • 值类别全面解析
    • char16_t 与 char32_t
    • static_assert 编译期断言
  • 第3章 函数与重载

    • 函数声明与定义
    • 函数重载
    • 默认参数与内联函数
    • Lambda 表达式
    • 函数对象与 stdfunction
    • 后置返回类型与 noexcept
  • 第4章 类与对象

    • 类的基本定义
    • 构造函数与析构函数
    • 拷贝控制
    • 移动构造函数与移动赋值
    • 列表初始化与类内初始化器
    • 静态成员与嵌套类
    • 友元
    • =default 与 =delete
  • 第5章 继承与多态

    • 继承基础
    • 虚函数与多态
    • 虚函数表与动态绑定原理
    • 虚析构函数
    • 抽象类与纯虚函数
    • 多重继承与虚继承
    • 继承构造函数
  • 第6章 运算符重载

    • 运算符重载基础
    • 算术与关系运算符重载
    • 赋值与移动运算符重载
    • 特殊运算符重载
  • 第7章 模板与泛型编程

    • 函数模板
    • 类模板
    • 模板特化与偏特化
    • 可变参数模板
    • 别名模板与模板模板参数
    • SFINAE 与类型萃取
    • 依赖名与 typename/template 关键字
  • 第8章 异常处理

    • 异常处理机制
    • noexcept 与异常安全
    • RAII 与异常安全实践
  • 第9章 内存管理与智能指针

    • 动态内存与内存分区
    • RAII 惯用法
    • unique_ptr
    • shared_ptr 与 weak_ptr
    • 内存管理最佳实践
  • 第10章 右值引用与移动语义

    • 右值引用与值类别深度解析
    • std::move 原理与使用
    • 完美转发与 std::forward
    • 移动语义性能对比与最佳实践
  • 第11章 STL容器

    • vector 深度剖析
    • deque 内部机制
    • list 与 forward_list
    • map 与 set 深度解析
    • unordered 容器与哈希原理
    • array 与 tuple
    • 容器适配器
    • 容器选择全景指南
  • 第12章 STL算法与迭代器

    • 迭代器体系全解
    • 非变异算法
    • 变异算法
    • 排序与二分算法
    • Lambda 与算法组合
    • std::random 随机数库
    • 自定义迭代器开发
  • 第13章 IO流与文件

    • 标准 IO 流
    • 格式化输出控制
    • 文件流操作
    • 字符串流
    • std::regex 正则表达式
  • 第14章 并发与多线程

    • thread 基础与线程管理
    • mutex 与 lock_guard
    • unique_lock 与 condition_variable
    • thread_local 线程局部存储
    • atomic 与内存序
    • future 与 async 异步编程
    • std::chrono 时间库
  • 第15章 现代C++新特性

    • 从 C++11 到 C++20 演进路线
    • C++14 关键新特性
    • C++17 关键新特性
    • C++20 核心特性速览
  • 第16章 面试考点与最佳实践

    • C++ 综合最佳实践清单
    • 高频面试题精讲
    • 多线程面试题与实战
    • 内存管理常见陷阱与排查
  • 附录

    • C++ 核心知识点
    • C++ 专业术语

格式化输出控制

定义与作用

格式化输出控制通过操纵符(manipulators)和格式标志调整流的输出格式——对齐、精度、进制、填充字符等。

#include <iomanip>

std::cout << std::setw(10) << std::setfill('0') << std::hex << 255;
// 输出: "00000000ff"

核心原理

格式标志体系

完整示例

示例一:飞翔科技财务报告

场景说明:高英用格式化输出为飞翔科技生成财务报告。

#include <iostream>
#include <iomanip>
#include <string>
#include <vector>

struct Revenue {
    std::string dept;
    double q1;
    double q2;
    double q3;
    double q4;
};

int main() {
    std::vector<Revenue> report = {
        {"技术部",  1888.88, 1666.66, 2320.00, 1888.88},
        {"产品部",  1234.56, 1456.78, 1678.90, 1890.12},
        {"运营部",  888.88,  999.99,  1111.11, 1234.56},
        {"设计部",  520.00,  666.66,  777.77,  888.88},
    };

    std::cout << "=== 飞翔科技 2026 年季度营收报告 ===\n\n";

    // 表头
    std::cout << std::left
              << std::setw(12) << "部门"
              << std::right
              << std::setw(12) << "Q1(万元)"
              << std::setw(12) << "Q2(万元)"
              << std::setw(12) << "Q3(万元)"
              << std::setw(12) << "Q4(万元)"
              << std::setw(14) << "合计(万元)"
              << "\n";
    std::cout << std::setfill('=') << std::setw(74) << "" << "\n";
    std::cout << std::setfill(' ');

    // 数据行
    double totals[4] = {0};
    for (const auto& r : report) {
        double sum = r.q1 + r.q2 + r.q3 + r.q4;
        std::cout << std::left  << std::setw(12) << r.dept
                  << std::right << std::fixed << std::setprecision(2)
                  << std::setw(12) << r.q1
                  << std::setw(12) << r.q2
                  << std::setw(12) << r.q3
                  << std::setw(12) << r.q4
                  << std::setw(14) << sum
                  << "\n";
        totals[0] += r.q1;
        totals[1] += r.q2;
        totals[2] += r.q3;
        totals[3] += r.q4;
    }

    // 合计行
    std::cout << std::setfill('-') << std::setw(74) << "" << "\n";
    std::cout << std::setfill(' ');
    double grandTotal = totals[0] + totals[1] + totals[2] + totals[3];
    std::cout << std::left  << std::setw(12) << "合计"
              << std::right << std::fixed << std::setprecision(2)
              << std::setw(12) << totals[0]
              << std::setw(12) << totals[1]
              << std::setw(12) << totals[2]
              << std::setw(12) << totals[3]
              << std::setw(14) << grandTotal
              << "\n";

    // 年增长率
    std::cout << "\n=== 营收指标 ===\n";
    std::cout << "年度总营收: " << std::setw(10) << grandTotal << " 万元\n";
    std::cout << "季度平均值: " << std::setw(10) << std::setprecision(2)
              << (grandTotal / 4) << " 万元\n";

    // 数字格式展示
    std::cout << "\n=== 数字格式展示 ===\n";
    int flags = 0xFF;  // 权限位掩码

    std::cout << "权限位(十进制): " << std::dec << flags << "\n";
    std::cout << "权限位(十六进制): " << std::hex << std::showbase
              << flags << "\n";
    std::cout << "权限位(八进制): " << std::oct << flags << "\n";
    std::cout << std::dec;  // 恢复十进制

    std::cout << "科学计数法: " << std::scientific << grandTotal << "\n";
    std::cout << std::fixed;  // 恢复定点

    // 布尔输出
    std::cout << "\n=== 系统状态 ===\n";
    std::cout << std::boolalpha
              << "  数据库连接: " << true << "\n"
              << "  缓存服务:   " << true << "\n"
              << "  消息队列:   " << false << "\n"
              << std::noboolalpha;
}

预期输出:

=== 飞翔科技 2026 年季度营收报告 ===

部门             Q1(万元)    Q2(万元)    Q3(万元)    Q4(万元)     合计(万元)
==========================================================================
技术部            1888.88     1666.66     2320.00     1888.88       7764.42
产品部            1234.56     1456.78     1678.90     1890.12       6260.36
运营部             888.88      999.99     1111.11     1234.56       4234.54
设计部             520.00      666.66      777.77      888.88       2853.31
--------------------------------------------------------------------------
合计              4532.32     4790.09     5887.78     5902.44      21112.63

=== 营收指标 ===
年度总营收: 21112.63 万元
季度平均值: 5278.16 万元

=== 数字格式展示 ===
权限位(十进制): 255
权限位(十六进制): 0xff
权限位(八进制): 0377
科学计数法: 2.111263e+04

=== 系统状态 ===
  数据库连接: true
  缓存服务:   true
  消息队列:   false

逐段分析:

  • setw 设置输出宽度(仅对下一次输出生效,需重复设置)
  • setprecision + fixed 控制小数点后位数
  • left / right 控制对齐方向
  • hex / oct / dec 切换进制;showbase 添加 0x / 0 前缀
  • scientific 切换科学计数法;需手动恢复到 fixed 或 defaultfloat
  • boolalpha 输出 true/false 而非 1/0

示例二:自定义 operator<<

场景说明:小崔为飞翔科技的 IP 地址结构实现自定义输出。

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <cstdint>

struct IPv4 {
    uint8_t a, b, c, d;

    IPv4(uint8_t a_, uint8_t b_, uint8_t c_, uint8_t d_)
        : a(a_), b(b_), c(c_), d(d_) {}

    // 自定义 operator<< 作为友元
    friend std::ostream& operator<<(std::ostream& os, const IPv4& ip) {
        return os << static_cast<int>(ip.a) << '.'
                  << static_cast<int>(ip.b) << '.'
                  << static_cast<int>(ip.c) << '.'
                  << static_cast<int>(ip.d);
    }
};

struct Server {
    std::string hostname;
    IPv4 ip;
    double load;
    std::string status;

    friend std::ostream& operator<<(std::ostream& os, const Server& s) {
        // 保存原始格式标志
        auto flags = os.flags();
        auto fill  = os.fill();

        os << std::left
           << std::setw(20) << s.hostname
           << std::setw(18) << s.ip
           << std::right
           << std::setw(8)  << std::fixed << std::setprecision(1)
           << s.load << "%"
           << "  "
           << std::left  << std::setw(8) << s.status;

        // 恢复格式
        os.flags(flags);
        os.fill(fill);
        return os;
    }
};

int main() {
    std::vector<Server> servers = {
        {"飞翔-Web-01",    IPv4(192, 168, 1, 101), 45.2, "在线"},
        {"飞翔-Web-02",    IPv4(192, 168, 1, 102), 62.8, "在线"},
        {"飞翔-DB-Master", IPv4(10, 0, 0, 1),      78.5, "在线"},
        {"飞翔-DB-Slave",  IPv4(10, 0, 0, 2),      12.3, "在线"},
        {"飞翔-Cache-01",  IPv4(172, 16, 0, 1),    91.0, "告警!"},
    };

    std::cout << "=== 飞翔科技服务器状态面板 ===\n\n";
    std::cout << std::left
              << std::setw(20) << "主机名"
              << std::setw(18) << "IP 地址"
              << std::setw(12) << "CPU 负载"
              << "状态\n";
    std::cout << std::setfill('-') << std::setw(58) << "" << "\n";
    std::cout << std::setfill(' ');

    for (const auto& s : servers)
        std::cout << s << "\n";

    std::cout << std::setfill('-') << std::setw(58) << "" << "\n";
    std::cout << "共 " << servers.size() << " 台服务器\n";
}

预期输出:

=== 飞翔科技服务器状态面板 ===

主机名              IP 地址            CPU 负载    状态
----------------------------------------------------------
飞翔-Web-01         192.168.1.101        45.2%  在线
飞翔-Web-02         192.168.1.102        62.8%  在线
飞翔-DB-Master      10.0.0.1             78.5%  在线
飞翔-DB-Slave       10.0.0.2             12.3%  在线
飞翔-Cache-01       172.16.0.1           91.0%  告警!
----------------------------------------------------------
共 5 台服务器

逐段分析:

  • 自定义 operator<< 可输出任意复杂结构
  • 保存/恢复 flags() 和 fill() 是良好习惯,避免污染调用者的格式
  • static_cast<int>(ip.a):uint8_t 会被视为 char 类型直接输出字符,需转换
  • friend 声明使 operator<< 可访问私有成员

易错场景与面试考点

易错场景

1. setw 仅对一次输出生效

std::cout << std::setw(10) << "A" << "B" << "\n";
//          ^^^^^^^^^^^^^^^^
//          只对 "A" 生效,"B" 不受影响

// 每次都需要 setw
std::cout << std::setw(10) << "A" << std::setw(10) << "B" << "\n";

2. hex/oct 粘性设置

std::cout << std::hex << 255 << " " << 100 << "\n";
// 输出: "ff 64" —— 所有后续数字都是十六进制!
std::cout << std::dec;  // 必须显式恢复

3. uint8_t 输出问题

uint8_t val = 65;
std::cout << val << "\n";  // 输出 'A' 而不是 65!
// 解决:static_cast<int>(val)

面试考点

考点要点
setw vs widthsetw 每调一次生效一字段,width() 返回当前宽度
粘性 vs 非粘性setw 非粘性(单次),其他大多粘性(持续)
setprecision配合 fixed 为小数位数,否则为有效数字位数
boolalphatrue/false 字符串 vs 1/0
showbasehex → 0x, oct → 0
internal 对齐符号靠左、数值靠右、中间填充
自定义 operator<<返回 ostream& 支持链式调用
格式恢复修改格式后应保存/恢复 flags()
上一页
标准 IO 流
下一页
文件流操作