如果你问大多数长期写 C++ 的人(尤其是用现代 C++ 的开发者),“C++ 最不应该存在的特性是什么?”,答案其实高度分散,但经常被反复点名的几个“罪魁祸首”集中在下面这几类(按被吐槽频率从高到低排序,2024–2025 社区共识视角):
Top 候选名单(社区最常提的“应该删掉/重做”的特性)
- C 风格数组 + 指针衰退(array-to-pointer decay) + 裸指针泛滥
几乎是压倒性票数第一的“原罪”。
- 为什么最该死:C++ 从 C 继承了“数组名退化为指针”的规则,导致
int a[10]; func(a);瞬间丢失大小信息,埋下无数越界、内存安全隐患。 - 现代 C++ 明明有
std::span、std::array、std::vector、std::string_view,但语言层面仍然允许/鼓励裸指针 + 衰退,导致新手/老代码继续写不安全代码。 - 很多人直言:“如果 C++ 一开始就把数组当成第一类对象(带边界检查),安全性起码提升一个数量级。”
- 隐式转换地狱(尤其是单参数构造函数 + 转换运算符)
bool operator bool()、operator int()、MyString(const char*)这些东西导致的意外转换、歧义、重载解析爆炸。- C++11 加了
explicit但默认还是允许,历史包袱太大。 - Herb Sutter 等人在安全 profile 里都想把大部分隐式转换干掉。
- 多重继承 + 虚继承(virtual inheritance)
- 菱形继承问题、虚基类指针偏移、内存布局不可预测、调试地狱。
- 现代 C++ 几乎没人敢用多继承实现(都用接口 + 组合),但语言还保留着完整的机制,增加理解成本和编译器复杂度。
- C 风格字符串(char* + strcpy / strlen / sprintf 家族)
- 缓冲区溢出之源,安全漏洞温床。
- C++ 提供了
std::string几十年了,但标准库和 C API 还在到处用const char*,强制大家写strlen、strcpy_s等防御代码。
- 头文件 + 重复包含 + 前向声明地狱
- 没有 modules 之前(C++20 Modules 还是半残),头文件管理是痛苦之最。
- 即使 C++20/23/26 Modules 来了,兼容旧代码的代价巨大,实际使用率低。
- 未定义行为(UB)密度过高
- 严格别名违规、signed overflow、null 指针解引用、越界、use-after-free 等 UB 太多,而且编译器可以随便“假设不发生”然后生成核弹级优化。
- 这不是单个特性,而是整个语言哲学的问题,但很多人觉得“默认允许 UB”是最大的设计失误。
如果只能选一个“最不应该存在”的,我个人投给:
数组名到指针的隐式衰退(array-to-pointer decay) + 裸指针作为默认参数/返回类型被允许
理由:
- 它直接导致了 C++ 前 30 年 70%+ 的内存安全 bug
- 现代替代方案(span / array / vector / ranges)已经非常成熟
- 保留这个规则纯粹是为了兼容 1972 年的 C,而这个兼容性代价现在已经远超收益
- Rust 直接把这个设计砍掉,证明了可以砍,而且砍了之后世界更美好
Bjarne Stroustrup 本人怎么看?
他公开说过几次“后悔”或“如果重来”的点(从访谈/论文中):
- 没有在早期就强制更强的类型安全
- 模板语法太复杂(concepts 晚了十几年)
- 允许太多的隐式行为和兼容 C 的代价
但他也反复强调:“没有哪个主要特性是应该完全删除的”,因为它们都有历史价值和使用场景。
结论(2026 年视角)
C++ 最不应该存在的特性其实不是某个单一功能,而是“为了兼容 C 而保留的大量危险默认行为”这个整体设计决策。
如果能时光倒流,大多数现代 C++ 开发者最想改的可能是:
“让 C++ 从一开始就默认更安全,而不是把安全当成 opt-in。”
你自己最讨厌 C++ 的哪个特性?是上面这些,还是别的更小众的雷(比如 union、goto、volatile、exception specifications 等)?