开场白
很多同学在纠结于选择哪门语言的时候,如果你去网上搜索 C++,通常搜索结果就是清一色的什么更贴近底层、内存优化、性能优势、做游戏、搞金融之类的。这篇文章的主要目的就是带你对 “C++ 为什么和这些标签高度绑定” 这件事有更清楚的认识,而不是以后别人问你 C++ 怎么样,你只会打标签但说不出个所以然来。
我们先从一个最常见的例子说起:
大家如果有刷一些算法题,会发现通常如果编程语言选的 C++ 的话,题目的时间限制一般是 1s,而如果是其他语言(比如 Python、Java 之类)通常是 2s 甚至更多。
这其实间接说明了 C++ 跑同样算法就是更快。比如排个序,C++ 0.3 秒完事,Python 可能要 1.5 秒。
那问题就来了:C++ 到底凭什么?
一、先看个最简单的:循环累加 1 亿次
C++ 写起来:
int sum = 0;
for (int i = 0; i < 100000000; ++i) sum += i;
编译后,sum 和 i 直接放在 CPU 寄存器里,循环展开、流水线优化,在 -O2 或 -O3 优化下大概 30~100 毫秒跑完。
Python 写同样的:
s = 0
for i in range(100000000):
s += i
Python 的整数是对象,每次 + 都要新建对象、检查类型、经过虚拟机。跑完起码 5 秒以上。
说白了:C++ 的代码编译完直接跑在 CPU 上,Python 的代码要交给解释器翻译成自己的指令再跑。多一层就慢一层。
也就是说 C++ 写出来就是机器码,Python 写出来要过一道虚拟机。
二、高级语法会不会拖慢速度?
有人担心:我用 vector 这种高级容器,会不会有额外开销?
这里就要提到 C++ 很出名的那句话了:
“你不用的东西,不需要为它付出代价”(You don‘t pay for what you don’t use)
看 vector<int>,你写 v.push_back(42),编译后就是几行内存操作指令,没有对象头、没有锁、没有 GC 屏障(垃圾回收)。
再看 Java 的 ArrayList<Integer>,每个 Integer 都是一个堆对象,读一个元素要跳好几次指针,还有 GC 随时可能卡你一下。
三、你的数据在内存里怎么摆,你说了算
很多语言你管不了数据在内存里是连续还是分散。C++ 可以。
举个例子,三维向量:
struct Vec3 { float x, y, z; };
vector<Vec3> points(1000000);
这 100 万个点在内存里是 连续 的一块:x,y,z,x,y,z...。CPU 预取、SIMD 一次性处理多个浮点数,快得很。
Java 里,ArrayList<Vec3> 每个 Vec3 是独立对象,散落在堆里。遍历的时候缓存根本用不上,性能差好几倍。
四、你能直接接触内存
很多语言里,你没法直接碰内存。比如 Java 的变量是引用,你改不了它的地址;Python 更是全自动。
C++ 让你用指针。指针就是一个数字,代表内存地址。你可以把它往前往后移,直接读写那块内存。
看个最简单的例子:
int arr[5] = {1,2,3,4,5};
int* p = arr; // p 指向数组第一个元素
p[2] = 100; // 直接改第三个元素的值为 100
这在很多语言里做不到(或者要用别扭的“不安全”模式)。
再比如,你要和一个硬件设备通信——设备把数据放在某个固定的内存地址上。C++ 可以这样写:
int* sensor = (int*)0x40000000;
int value = *sensor;
别的语言没有这种“直接给地址”的写法。
顺带一提:这段代码只能在嵌入式裸机或驱动开发中才能合法,在普通操作系统中会触发段错误,因为你无权访问物理内存。
总结一下:C++ 让你自己动手操作内存地址,而不是交给虚拟机托管。
五、能帮编译器省活儿,编译期就算完了
C++ 把很多计算挪到编译期,运行时几乎不留痕迹。
比如编译期算阶乘:
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
int arr[factorial(5)]; // 编译期就算出 120,数组大小固定
如果没有 constexpr,类似的“编译期常量”在 C 语言里只能用宏(不安全)或者硬编码(不灵活)。在 Java 或 C# 中,常量表达式有部分编译期折叠能力,但像函数调用这种复杂的编译期计算,直到近年才逐步支持(Java 的常量表达式有限,C# 的 const 只支持字面量,不能调用函数)。而 C++ 的 constexpr 函数可以像普通函数一样写逻辑,完全在编译期执行,既安全又不占运行时 CPU。
还有模板:vector<int> 和 vector<float> 会生成两套独立代码,各优化各的。Java 的泛型是擦除式的,运行时还要转换。
小 lambda 也经常被内联,函数调用都没了。
六、落实到现实需求
金融交易
大致流程:收行情 → 算价格 → 发单子。要求:追求零 GC、极少的锁和系统调用(除了网卡)。
C++ 可以做到:
- 内存池,全程不
new - 线程绑核,不让操作系统乱调度
- 直接用
rdtsc指令读 CPU 时钟
高频交易领域通常倾向避免 GC, 因为任何不可预测延迟都可能带来风险。
游戏行业
- 引擎底层(Unreal、自研):渲染、物理、音频、网络。主流商业游戏引擎底层长期以 C++ 为主,因为每帧只有 16 毫秒(60 帧),要直接操作 GPU 和内存。
- 游戏逻辑(技能、AI、UI):用 C# (Unity) 或 Lua。改技能不用重编译引擎,开发快。
底层基础设施
- 数据库:MySQL、RocksDB 的核心是 C++,要直接操作磁盘(
O_DIRECT)、手写内存池。 - 浏览器:Chrome 的 Blink、V8 都是 C++,解析 HTML 跑 JS 要飞快。
- 消息队列:ZeroMQ 也是 C++,处理 TCP 和并发。
嵌入式
路由器、汽车仪表、医疗设备。直接写寄存器点灯,不经过操作系统。
*((uint32_t*)0x40021000) |= (1 << 5); // 亮灯
Python 干不了这活,因为它得跑在操作系统上。
七、C++ 的困境
这里我想多提一嘴,简单说一下 C++ 被人一直诟病的地方。其实如果你刚开始学 C++,遇到最大的困难应该其实是各种各样的语法,一堆莫名其妙的初始化方式,你完全不理解为什么要这样设计。但其实这只是 C++ 很小的一部分问题。
1. C++ 一直被吐槽的那些点(只是很少的一部分)
- 头文件:改一个头文件,整个项目重编译半小时。
- 模板报错:写错一点,编译器喷你 200 行错误,根本看不懂。
- 没有标准包管理:C++ 搞个依赖比 Java 麻烦十倍。
这些痛苦都是为了 性能和控制 付出的代价。Java 没这些坑,但也摸不到内存和 CPU。
2. Rust 会不会取代 C++?
Rust 解决了 C++ 的一个大痛点:内存安全。
C++ 里迭代器很容易失效(你 push_back 后之前的迭代器就不能用了),Rust 编译器直接不允许:
let mut v = vec![1,2,3];
let it = v.iter();
v.push(4); // 编译错误!不能同时存在可变和不可变借用
这就防止了一大类崩溃。而且 Rust 的工具链(cargo)比 C++ 好太多了。
但众所周知,一个语言最重要的是找到属于它自己的生态位,而 C++ 已经在众多行业领域深耕多年,目前来看,Rust 在国内的发展路还很长。