第四章,效率

23-考虑使用其他程序库

在检索程序性能瓶颈时,要把引用的程序库这一因素考虑进来,可以思考是否可以替换程序库或修改程序库来提升性能

举例,在以前,iostream库明显慢于stdio,即使iostream有类型安全特性,并且可以扩充(采用面向对象设计,允许用户自定义类型无缝接入流操作);如今可以使用下面的语句设置来得到一个高效的iostream

// 关闭与C标准IO的同步
std::ios::sync_with_stdio(false);
// 解除cin与cout的绑定
std::cin.tie(nullptr);

//这也是iostream比stdio慢的原因:
//1、与stdio同步: cin/cout默认与scanf/printf保持同步,确保混用时输出顺序正确,但带来巨大花销
//2、流绑定:cin默认绑定cout,每次输入前自动刷新输出缓冲区

引申: iostream扩充

1、运算符重载(最常用)
2、自定义流缓冲区: 通过继承std::streambuf实现自定义底层I/O

#include <streambuf>
#include <ostream>

// 示例:日志流缓冲区,自动添加时间戳
class LogStreamBuf : public std::streambuf {
protected:
std::streambuf* dest;
bool at_line_start = true;

virtual int overflow(int c) override {
if (at_line_start && c != '\n') {
const char* prefix = "[2026-03-19 14:00] ";
dest->sputn(prefix, strlen(prefix));
}
at_line_start = (c == '\n');
return dest->sputc(c);
}

public:
LogStreamBuf(std::streambuf* d) : dest(d) {}
};

// 使用自定义缓冲区
class LogStream : public std::ostream {
LogStreamBuf buf;
public:
LogStream(std::ostream& dest)
: std::ostream(&buf), buf(dest.rdbuf()) {}
};

LogStream log(std::cout);
log << "Hello World" << std::endl; // 输出: [2026-03-19 14:00] Hello World

/*
输出效果等同:
const char* prefix = "[2026-03-19 14:00] ";
cout.rdbuf()->sputn(prefix, strlen(prefix));//向缓冲区写入字串
cout << "Hello World" << std::endl;
*/

3、自定义locale/facet

#include <locale>
#include <iomanip>

// 自定义数字分隔符 facet
struct MoneyPunct : public std::numpunct<char> {
protected:
virtual char do_thousands_sep() const override { return ','; }
virtual std::string do_grouping() const override { return "\3"; } // 每3位分组
};

// 使用
std::locale loc(std::locale::classic(), new MoneyPunct);
std::cout.imbue(loc);
std::cout << std::fixed << 12345678.90; // 输出: 12,345,678.90

4、自定义操纵器(Manipulators)

#include <iostream>
#include <iomanip>

// 自定义操纵器:设置颜色(简化示例)
struct Color {
int code;
Color(int c) : code(c) {}
};

std::ostream& operator<<(std::ostream& os, const Color& c) {
// 实际实现会输出 ANSI 转义码
return os << "\033[" << c.code << "m";
}

// 便利函数
Color red(31), green(32), reset(0);

// 用法
std::cout << red << "Error: " << reset << "File not found\n";

这些东西,也许在制作一个调试系统的时候会有用…