一、C++ 特有语法错误(易与 C 混淆或忽略)
C++ 兼容 C 的大部分语法,但新增了面向对象、模板等特性,也带来了独特的语法陷阱。
1. 动态内存管理不匹配(new/delete 配对错误)
C++ 用new/new[]动态分配内存,必须用对应的delete/delete[]释放,否则会导致内存泄漏或未定义行为(如崩溃)。
- 错误示例:
cpp
运行
int* p1 = new int[5]; // 用new[]分配数组 delete p1; // 错误:应使用delete[]释放数组,否则仅释放首元素,其余内存泄漏 int* p2 = new int; // 用new分配单个元素 delete[] p2; // 错误:用delete[]释放单个元素,可能触发崩溃 - 避免方法:
- 牢记 “配对原则”:
new↔delete,new[]↔delete[]; - 优先使用智能指针(
unique_ptr/shared_ptr),自动管理内存,无需手动释放:cpp
运行
#include <memory> unique_ptr<int[]> p(new int[5]); // 自动释放,无需delete[]
- 牢记 “配对原则”:
2. 类成员未初始化(默认构造函数陷阱)
C++ 类的成员变量(尤其是内置类型,如int/double)若未在构造函数初始化列表或声明时初始化,会处于 “随机值” 状态,导致逻辑错误。
- 错误示例:
cpp
运行
class Student { public: int age; // 内置类型成员,未初始化 Student() {} // 默认构造函数未处理age }; int main() { Student s; cout << s.age; // 错误:age是随机值,结果不可控 } - 避免方法:
- 用初始化列表初始化成员(推荐,效率更高):
cpp
运行
Student() : age(0) {} // 初始化列表将age设为0 - C++11 及以后,可直接在成员声明时初始化:
cpp
运行
int age = 0; // 声明时初始化,默认构造函数会沿用
- 用初始化列表初始化成员(推荐,效率更高):
3. 访问权限错误(public/private/protected 混淆)
C++ 类的成员默认是private(私有),若未显式声明为public,外部无法访问,会导致编译错误。
- 错误示例:
cpp
运行
class Person { string name; // 默认private void show() { cout << name; } // 默认private }; int main() { Person p; p.name = "Tom"; // 错误:name是private,外部无法赋值 p.show(); // 错误:show()是private,外部无法调用 } - 避免方法:
- 明确成员的访问权限:
public(外部可访问,如接口函数)、private(仅类内部访问,如数据成员)、protected(类内部及子类访问):cpp
运行
class Person { private: string name; // 数据成员私有 public: void setName(string n) { name = n; } // 公有接口 void show() { cout << name; } // 公有接口 };
- 明确成员的访问权限:
二、C++ 运行时错误(高频场景)
1. STL 容器迭代器失效(最易踩坑的 STL 问题)
STL 容器(如vector/list/map)在插入、删除元素后,部分迭代器会 “失效”(指向非法内存),继续使用会导致段错误。
- 错误示例(
vector迭代器失效):cpp
运行
vector<int> vec = {1,2,3}; for (auto it = vec.begin(); it != vec.end(); ++it) { if (*it == 2) { vec.erase(it); // 删除元素后,it立即失效 } cout << *it; // 错误:it失效后访问,触发段错误 } - 避免方法:
- 利用
erase()的返回值:erase()会返回 “下一个有效迭代器”,用其更新迭代器:cpp
运行
for (auto it = vec.begin(); it != vec.end(); ) { // 注意:不在这里++it if (*it == 2) { it = vec.erase(it); // 用返回值更新it,指向有效位置 } else { ++it; // 仅当未删除时,迭代器才后移 } } - 不同容器迭代器失效规则不同(如
list删除元素后,其他迭代器不失效),需熟记容器特性。
- 利用
2. 虚函数与多态错误(未定义行为)
C++ 多态依赖虚函数,若子类重写虚函数时 “签名不匹配”(如参数、返回值不同),会导致多态失效;若通过 “空指针调用虚函数”,会触发段错误。
- 错误示例 1:虚函数签名不匹配(多态失效)
cpp
运行
class Base { public: virtual void func(int x) { cout << x; } // 基类虚函数:参数int }; class Derived : public Base { public: virtual void func(double x) { cout << x; } // 子类“重写”:参数double(签名不匹配) }; int main() { Base* p = new Derived(); p->func(5); // 错误:多态失效,调用的是基类func(int),而非子类func(double) } - 错误示例 2:空指针调用虚函数
cpp
运行
Base* p = NULL; p->func(5); // 错误:p是空指针,调用虚函数时无法访问虚函数表,触发段错误 - 避免方法:
- 子类重写虚函数时,确保函数名、参数列表、返回值(协变除外)完全一致;
- C++11 起用
override关键字强制检查重写有效性,不匹配会编译报错:cpp
运行
virtual void func(double x) override { ... } // 若基类无func(double),编译报错 - 调用虚函数前,确保指针 / 引用指向有效对象(非空)。
3. 数组越界(C++ 中更隐蔽的场景)
除了传统数组(如int a[5])越界,vector的[]运算符也不检查越界(仅at()会抛异常),容易导致未定义行为。
- 错误示例:
cpp
运行
vector<int> vec(3, 0); // vec有3个元素:[0,0,0] vec[5] = 10; // 错误:越界访问,无编译错误,但运行时可能崩溃或破坏内存 - 避免方法:
- 用
vector::at()访问元素,越界时会抛out_of_range异常,便于调试:cpp
运行
try { vec.at(5) = 10; // 抛异常,可捕获处理 } catch (out_of_range& e) { cout << "越界错误:" << e.what(); } - 访问前检查索引:
if (idx < vec.size()) { ... }。
- 用
三、C++ 核心避坑原则
- 优先使用 STL 和智能指针:避免手动管理内存(减少内存泄漏、野指针),用
vector替代传统数组(动态扩容、size()获取长度)。 - 编译时启用高警告级别:如 GCC 的
-Wall -Wextra、VS 的/W4,可提前捕获大量潜在错误(如未初始化变量、虚函数重写不匹配)。 - 善用调试工具:用 GDB(Linux)、Visual Studio 调试器(Windows)定位运行时错误(如段错误时查看调用栈、内存地址);用 Valgrind(Linux)检测内存泄漏。
- 遵循 C++ 编码规范:如成员变量加前缀(
m_name)、虚函数用override、动态内存用智能指针,提升代码可读性和安全性。
2656

被折叠的 条评论
为什么被折叠?



