编程错误介绍

一、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 ↔ deletenew[] ↔ 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++ 核心避坑原则

  1. 优先使用 STL 和智能指针:避免手动管理内存(减少内存泄漏、野指针),用vector替代传统数组(动态扩容、size()获取长度)。
  2. 编译时启用高警告级别:如 GCC 的-Wall -Wextra、VS 的/W4,可提前捕获大量潜在错误(如未初始化变量、虚函数重写不匹配)。
  3. 善用调试工具:用 GDB(Linux)、Visual Studio 调试器(Windows)定位运行时错误(如段错误时查看调用栈、内存地址);用 Valgrind(Linux)检测内存泄漏。
  4. 遵循 C++ 编码规范:如成员变量加前缀(m_name)、虚函数用override、动态内存用智能指针,提升代码可读性和安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值