C++ 学习笔记(一):从 C 到 C++,你已经会的和需要转变的
如果你已经用 C 写过嵌入式项目,上手 C++ 的成本比你想象的低——大部分 C 代码可以直接放进 .cpp 文件编译通过。真正需要转变的不是语法,而是思维方式:C 是”告诉机器怎么做”,C++ 还多了一层”把相关的数据和操作组织在一起”。
这篇文章不从头教 C++,而是专门讲 C 开发者切换到 C++ 时会碰到的差异点。
一、引用:比指针少一个 *,但不止于此
C 里传指针是家常便饭:
1 | void swap(int *a, int *b) { |
C++ 里用引用:
1 | void swap(int &a, int &b) { |
语义上:引用是变量的别名,不是新变量。声明时必须初始化,之后不能改变绑定的对象。
1 | int x = 10; |
和指针的关键区别:
| 指针 | 引用 | |
|---|---|---|
| 可以为 null | ✅ | ❌ |
| 可以重新绑定 | ✅ | ❌ |
需要解引用 * |
✅ | ❌ |
| 声明时必须初始化 | ❌ | ✅ |
嵌入式场景里,函数参数用引用的好处:不需要判断空指针(引用天生不为 null),调用点代码更干净。
const 引用:只读不写
1 | void print_data(const SensorData &data) { |
传大结构体首选 const &——既避免了拷贝,又明确了函数不修改入参。
二、函数重载:同一个名字,不同参数
C 里你可能写过:
1 | int abs_int(int x) { return x < 0 ? -x : x; } |
C++ 允许同名函数:
1 | int abs(int x) { return x < 0 ? -x : x; } |
编译器根据参数类型选择调用哪个,这叫重载决议。
注意:返回类型不同不构成重载。下面这个无法编译:
1 | int get_value() { return 1; } |
三、默认参数
1 | void delay(uint32_t ms, bool blocking = true) { |
默认参数必须从右往左连续设置:
1 | void func(int a, int b = 10, int c = 20); // ✅ |
嵌入式里常用于初始化函数:HAL_UART_Init(&huart1) 这类 API 如果用 C++ 写,可以把常用参数设默认值,减少重复配置代码。
四、命名空间:解决名字冲突
C 里经典问题:两个库都定义了 init() 函数,链接时冲突。C 的解法是加前缀:hal_init()、rtos_init()。
C++ 用命名空间:
1 | namespace HAL { |
using 可以省略命名空间前缀:
1 | using namespace HAL; |
但在嵌入式项目里,不推荐在头文件里写 using namespace——头文件被多处 include,会把命名空间污染传播出去。在 .cpp 文件里局部使用没问题。
五、new 和 delete
C 里动态分配:
1 | int *p = (int *)malloc(sizeof(int)); |
C++ 里:
1 | int *p = new int(42); // 分配 + 初始化 |
new 做了两件事:分配内存 + 调用构造函数。delete 做了两件事:调用析构函数 + 释放内存。
嵌入式里的态度:
裸机 MCU 上谨慎使用 new/delete——动态内存的碎片问题和 malloc/free 一样存在。更推荐用对象池或栈上对象。但对象的初始化语义(构造函数)仍然值得用,只是分配方式可以是静态的。
1 | // 不用 new,但用构造函数语义 |
六、nullptr,不是 NULL
C 里的 NULL 本质是 0 或 (void*)0,有时会让重载产生歧义:
1 | void func(int x) { } |
C++ 11 开始用 nullptr,类型是 std::nullptr_t,不会和整数混淆。规则很简单:C++ 代码里只用 nullptr,不用 NULL。
七、// 注释和 /* */ 都能用
这个不用解释,C++ 兼容 C 的块注释,同时支持单行 //。唯一提一下:C99 之后 C 也支持 //,所以这不是 C++ 独有的。
八、类型转换:用 C++ 风格,别用 C 风格
C 里强制类型转换:
1 | float f = 3.14f; |
C++ 提供四种具名转换,更安全、更清晰:
1 | // static_cast:最常用,编译期检查 |
嵌入式最常用的两个:static_cast 用于普通类型转换,reinterpret_cast 用于寄存器地址映射。
C 风格的 (type)value 在 C++ 里也能编译,但不推荐——它太宽泛了,会悄悄帮你做 reinterpret_cast 级别的危险转换而不报错。
九、bool 是内置类型
C 里没有原生 bool(C99 才有 _Bool,要 #include <stdbool.h>)。C++ 里 bool 是内置类型:
1 | bool flag = true; |
true 转 int 是 1,false 是 0。非零整数转 bool 是 true,0 是 false。
十、inline 函数替代宏
C 里用宏做简单函数:
1 |
宏的问题:没有类型检查、调试困难、MAX(a++, b++) 会让 a 或 b 自增两次。
C++ 里用 inline 函数:
1 | inline int max(int a, int b) { |
有类型检查,行为可预期,调试器能进入函数体。现代编译器会自动决定是否内联,inline 关键字在优化方面的意义已经很小,主要用于解决头文件中函数定义的多重定义问题。
模板函数可以完全替代类型不固定的宏:
1 | template<typename T> |
十一、struct 不用写 typedef
C 里:
1 | typedef struct { |
C++ 里 struct 名字直接是类型名:
1 | struct Point { |
C++ 的 struct 和 class 几乎一样——唯一区别是 struct 默认成员是 public,class 默认是 private。
总结
从 C 切换到 C++,最先感受到的变化:
- 引用让函数接口更干净,不用到处写
*和& - 函数重载和默认参数减少了
_int/_float这类后缀函数 - 命名空间解决了大项目的名字冲突问题
nullptr、具名转换、bool、inline是 C++ 提供的更安全的替代品





