输入关键词开始搜索

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)