引言:为什么C++困难但有趣
C++是功能最强大、性能最高的编程语言之一。但能力越大,责任越大。初学者开发者经常遇到导致程序崩溃、内存泄漏和未定义行为的错误。在本文中,我们将逐一解析初学者在C++中常犯的10个最典型错误。你将学会如何识别、修复它们,更重要的是,如何在未来避免它们。这份材料对学生和刚踏入系统编程领域的人都会很有帮助。
1. 忽视未定义行为(UB)
最隐蔽的错误之一是未定义行为。编译器不会发出警告,程序可能看起来"正常运行",但当优化选项或平台改变时,一切都会崩溃。
示例:数组越界访问
int arr[5] = {1, 2, 3, 4, 5};arr[5] = 42; // UB:越界写入std::cout << arr[10]; // UB:读取垃圾值如何避免:始终使用支持边界检查的std::array或std::vector(通过.at())以及STL容器。永远不要依赖"运气"——未定义行为可能在最糟糕的时刻爆发。
2. 内存泄漏和原始指针
C++没有垃圾回收器。每个new必须有对应的delete。初学者经常忘记释放内存,尤其是在发生异常的情况下。
泄漏示例
void leak() { int* p = new int(100); // 忘记 delete p; // 函数退出时内存丢失}解决方案:使用智能指针:独占所有权用std::unique_ptr,共享所有权用std::shared_ptr。它们会在离开作用域时自动释放内存。
void no_leak() { auto p = std::make_unique<int>(100); // 自动调用 delete}3. 引用和指针的错误使用
混淆引用(&)和指针(*)是常见问题。
- 引用是别名。不能为
nullptr,不能被重新赋值。 - 指针可以为空,可以指向不同对象。
典型错误:返回局部对象的引用
int& bad_ref() { int x = 10; return x; // 退出后x被销毁}// 用法:int& r = bad_ref(); // 悬空引用规则:永远不要返回指向局部变量的引用或指针。只返回生命周期有保障的数据(例如通过引用传递的参数或静态变量)。
4. 忽视RAII(资源获取即初始化)
RAII是C++的关键惯用法。资源(内存、文件、互斥锁)在构造函数中获取,在析构函数中释放。初学者常常手动管理,导致异常发生时出现泄漏。
错误示例
void handle_file() { FILE* f = fopen("data.txt", "r"); // ... 处理文件 ... if (some_error) { return; // 忘记 fclose! } fclose(f);}使用RAII的正确示例
void handle_file_safe() { std::ifstream f("data.txt"); if (!f) return; // ... 处理 ... // 退出作用域时文件自动关闭}结论:始终用RAII包装器封装资源。文件用std::fstream,内存用智能指针,互斥锁用std::lock_guard。
5. 忽视const和constexpr
许多初学者不使用