C++ 常见 Bug 合集 — 高频陷阱与修复
🔴 高频 + 高难度
1. 悬空引用 / 悬空指针
频率: ⭐⭐⭐⭐⭐ 难度: ⭐⭐⭐⭐
// ① 返回局部变量的引用
const string &getMessage() {
string msg = "hello";
return msg; // ❌ msg 已析构 → 悬空引用
}
// ✅ 返回值而非引用
string getMessage() { return "hello"; }
// ② 容器元素引用在 realloc 后失效
vector<int> v = {1, 2, 3};
int &ref = v[0];
v.push_back(4); // realloc! ref 悬空
cout << ref; // ❌ 未定义行为
// ③ Lambda 按引用捕获 + 延迟执行
function<void()> createCallback() {
int x = 42;
return [&x] { cout << x; }; // ❌ x 已析构
}
2. 迭代器失效
频率: ⭐⭐⭐⭐⭐ 难度: ⭐⭐⭐
// ① 插入/删除导致 rehash
vector<int> v = {1, 2, 3, 4, 5};
for (auto it = v.begin(); it != v.end(); ++it) {
if (*it % 2 == 0) v.erase(it); // ❌ it 失效
}
// ✅
for (auto it = v.begin(); it != v.end(); ) {
if (*it % 2 == 0) it = v.erase(it); // erase 返回下一个有效迭代器
else ++it;
}
// ② map/unordered_map 遍历中删除
map<int, string> m;
for (auto it = m.begin(); it != m.end(); ) {
if (it->second.empty()) it = m.erase(it);
else ++it;
}
// C++20: std::erase_if(m, [](auto &p) { return p.second.empty(); });
3. 多线程数据竞争
频率: ⭐⭐⭐⭐ 难度: ⭐⭐⭐⭐⭐
// ❌ 最简单的竞争
int counter = 0;
void thread1() { for (int i = 0; i < 1000000; ++i) ++counter; }
void thread2() { for (int i = 0; i < 1000000; ++i) ++counter; }
// counter 最终 ≠ 2000000,且每次结果不同
// ✅
atomic<int> counter{0};
// 或: mutex + lock_guard
// ❌ 看似安全但实际危险的 Singleton
Singleton *Singleton::get() {
if (!instance) { // ① 线程 A 读到 nullptr
lock(); // ② A 加锁
if (!instance) // ③ DCLP — 在 C++11 前有重排风险
instance = new Singleton; // ④ B 可能看到半构造对象
unlock();
}
return instance;
}
// ✅ C++11+: 函数内 static 天然线程安全
Singleton &get() { static Singleton s; return s; }
🟡 中频 + 中难度
4. 未定义行为 (UB) 合集
// ① 有符号整数溢出
int x = INT_MAX;
x += 1; // ❌ UB!编译器可以假设永远不会溢出
// ② 空指针解引用
int *p = nullptr;
*p = 42; // ❌ 程序可能不崩溃(取决于平台)
// ③ 越界访问
int arr[10];
arr[10] = 0; // ❌ UB,可能破坏相邻变量
// ④ 使用已释放的内存 (use-after-free)
auto *p = new int(42);
delete p;
*p = 100; // ❌ UB
// ⑤ 违反 strict aliasing
float f = 3.14f;
int i = *(int *)&f; // ❌ UB
// ✅ memcpy 或 std::bit_cast (C++20)
int i; memcpy(&i, &f, sizeof(i));
5. 内存泄漏
// ① 异常导致 delete 被跳过
void unsafe() {
auto *p = new Resource;
mightThrow(); // 抛异常 → delete 永远不执行
delete p;
}
// ✅ unique_ptr
void safe() {
auto p = make_unique<Resource>();
mightThrow(); // p 自动销毁
}
// ② 数组用错 delete
int *arr = new int[100];
delete arr; // ❌ UB — 应该 delete[]
// ③ 循环引用(shared_ptr)
class A { shared_ptr<B> b; };
class B { shared_ptr<A> a; }; // 互相持有 → 永不释放
// ✅ 一方用 weak_ptr
6. ODR 违反(One Definition Rule)
// a.cpp
int GLOBAL = 42;
// b.cpp
double GLOBAL = 3.14; // ❌ 同名不同类型 → ODR 违反
// header.h
int counter; // ❌ 头文件里的非 const 变量 → 每个 .cpp 一份 → 链接错误
// ✅ inline (C++17) 或 extern + .cpp 中定义
7. 虚函数默认参数陷阱
class Base {
public:
virtual void f(int n = 10) { cout << "Base: " << n; }
};
class Derived : public Base {
public:
void f(int n = 20) override { cout << "Derived: " << n; }
};
Base *p = new Derived();
p->f(); // "Derived: 10" ← 默认参数来自 Base(静态绑定),函数体来自 Derived
🟢 低频 + 低难度
8. 模板编译不过:缺少 typename
template <typename T>
void foo() {
T::value_type x; // ❌ 编译器不知道 T::value_type 是类型还是变量
typename T::value_type x; // ✅
}
9. 头文件循环依赖
// a.h 包含 b.h,b.h 包含 a.h → 无限递归
// ✅ 头文件用 #pragma once + 前置声明 + cpp 中包含
10. CMake 中忘记编译新文件
# 新增了 parser.cpp 但没加到 CMake
add_executable(myapp main.cpp) # ❌ 少了 parser.cpp
# → 链接时 undefined reference to Parser::parse()
11. 类型转换精度丢失
double d = 1.999;
int i = d; // 1 — 截断,不是四舍五入
int i = static_cast<int>(d + 0.5); // 2 — 手动四舍五入
size_t n = 0;
for (int i = 0; i < n - 1; ++i) // ❌ n-1 = SIZE_MAX → 无限循环!
// ✅ for (size_t i = 0; i + 1 < n; ++i)