第5章:错误处理与调试

🎯 学习目标

  • 掌握错误处理机制
  • 学会使用调试工具
  • 理解异常处理(C++)
  • 掌握基本的调试技巧
  • 了解代码规范和最佳实践

📅 学习时间安排

  • 总时长: 15-20小时
  • 理论学习: 6小时
  • 编程实践: 12小时
  • 代码量目标: 300行

第一章:错误处理基础

1.1 错误处理的重要性

在编程中,错误处理是确保程序稳定性和可靠性的关键。良好的错误处理可以:

  • 防止程序崩溃
  • 提供有用的错误信息
  • 帮助快速定位和修复问题
  • 提高用户体验

1.2 C语言错误处理机制

1.2.1 返回值检查

C语言主要通过函数返回值来指示错误状态:

#include <stdio.h>
#include <stdlib.h>

// 示例:文件操作错误处理
int read_file(const char* filename) {
    FILE* file = fopen(filename, "r");
    if (file == NULL) {
        printf("错误:无法打开文件 %s\n", filename);
        return -1;  // 返回错误码
    }
    
    // 读取文件内容...
    
    fclose(file);
    return 0;  // 成功返回0
}

// 示例:内存分配错误处理
int* create_array(int size) {
    int* array = (int*)malloc(size * sizeof(int));
    if (array == NULL) {
        printf("错误:内存分配失败\n");
        return NULL;  // 返回NULL表示错误
    }
    return array;
}
1.2.2 errno机制

C标准库提供了全局变量errno来指示错误类型:

#include <stdio.h>
#include <errno.h>
#include <string.h>

void demonstrate_errno() {
    FILE* file = fopen("nonexistent.txt", "r");
    if (file == NULL) {
        printf("错误号: %d\n", errno);
        printf("错误信息: %s\n", strerror(errno));
        perror("perror输出");  // 自动添加错误信息
    }
}

// 自定义错误处理函数
void handle_error(const char* operation) {
    fprintf(stderr, "%s 失败: %s (错误号: %d)\n", 
            operation, strerror(errno), errno);
}
1.2.3 错误码定义

定义清晰的错误码有助于错误处理:

// 自定义错误码
typedef enum {
    SUCCESS = 0,
    ERROR_FILE_NOT_FOUND = -1,
    ERROR_PERMISSION_DENIED = -2,
    ERROR_OUT_OF_MEMORY = -3,
    ERROR_INVALID_PARAM = -4,
    ERROR_NETWORK_TIMEOUT = -5
} ErrorCode;

// 错误码转换函数
const char* error_to_string(ErrorCode code) {
    switch (code) {
        case SUCCESS: return "成功";
        case ERROR_FILE_NOT_FOUND: return "文件未找到";
        case ERROR_PERMISSION_DENIED: return "权限被拒绝";
        case ERROR_OUT_OF_MEMORY: return "内存不足";
        case ERROR_INVALID_PARAM: return "参数无效";
        case ERROR_NETWORK_TIMEOUT: return "网络超时";
        default: return "未知错误";
    }
}

1.3 C++异常处理机制

1.3.1 基本语法

C++提供了更强大的异常处理机制:

#include <iostream>
#include <stdexcept>
#include <string>

void demonstrate_basic_exceptions() {
    try {
        int divisor = 0;
        if (divisor == 0) {
            throw std::runtime_error("除数不能为零");
        }
        int result = 10 / divisor;
        std::cout << "结果: " << result << std::endl;
    }
    catch (const std::exception& e) {
        std::cerr << "捕获异常: " << e.what() << std::endl;
    }
}
1.3.2 异常类型层次

标准异常类的层次结构:

#include <exception>
#include <stdexcept>
#include <new>
#include <typeinfo>

void demonstrate_exception_hierarchy() {
    try {
        // 可能抛出不同类型的异常
        int choice;
        std::cout << "选择异常类型 (1-5): ";
        std::cin >> choice;
        
        switch (choice) {
            case 1:
                throw std::runtime_error("运行时错误");
            case 2:
                throw std::logic_error("逻辑错误");
            case 3:
                throw std::out_of_range("超出范围");
            case 4:
                throw std::bad_alloc();  // 内存分配失败
            case 5:
                throw std::invalid_argument("无效参数");
        }
    }
    catch (const std::bad_alloc& e) {
        std::cerr << "内存分配异常: " << e.what() << std::endl;
    }
    catch (const std::out_of_range& e) {
        std::cerr << "范围异常: " << e.what() << std::endl;
    }
    catch (const std::invalid_argument& e) {
        std::cerr << "参数异常: " << e.what() << std::endl;
    }
    catch (const std::exception& e) {
        std::cerr << "标准异常: " << e.what() << std::endl;
    }
    catch (...) {
        std::cerr << "未知异常" << std::endl;
    }
}
1.3.3 自定义异常类

创建自定义异常类:

#include <string>
#include <exception>

// 自定义异常类
class FileException : public std::exception {
private:
    std::string message;
    std::string filename;
    
public:
    FileException(const std::string& msg, const std::string& file) 
        : message(msg), filename(file) {}
    
    const char* what() const noexcept override {
        return ("文件错误: " + message + " (" + filename + ")").c_str();
    }
    
    const std::string& get_filename() const { return filename; }
};

class NetworkException : public std::exception {
private:
    std::string message;
    int error_code;
    
public:
    NetworkException(const std::string& msg, int code) 
        : message(msg), error_code(code) {}
    
    const char* what() const noexcept override {
        return ("网络错误: " + message).c_str();
    }
    
    int get_error_code() const { return error_code; }
};

1.4 日志记录系统

1.4.1 简单日志系统

实现基本的日志记录功能:

#include <stdio.h>
#include <time.h>
#include <stdarg.h>

typedef enum {
    LOG_DEBUG,
    LOG_INFO,
    LOG_WARNING,
    LOG_ERROR,
    LOG_FATAL
} LogLevel;

// 日志级别名称
const char* log_level_names[] = {
    "DEBUG", "INFO", "WARNING", "ERROR", "FATAL"
};

// 日志函数
void write_log(LogLevel level, const char* format, ...) {
    FILE* log_file = fopen("application.log", "a");
    if (log_file == NULL) return;
    
    // 获取当前时间
    time_t now = time(NULL);
    struct tm* time_info = localtime(&now);
    char time_str[20];
    strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", time_info);
    
    // 输出时间戳和日志级别
    fprintf(log_file, "[%s] [%s] ", time_str, log_level_names[level]);
    
    // 输出日志内容
    va_list args;
    va_start(args, format);
    vfprintf(log_file, format, args);
    va_end(args);
    
    fprintf(log_file, "\n");
    fclose(log_file);
    
    // 如果是致命错误,同时输出到控制台
    if (level >= LOG_ERROR) {
        printf("[%s] [%s] ", time_str, log_level_names[level]);
        va_list console_args;
        va_start(console_args, format);
        vprintf(format, console_args);
        va_end(console_args);
        printf("\n");
    }
}
1.4.2 C++日志类

使用面向对象方式实现日志系统:

#include <iostream>
#include <fstream>
#include <string>
#include <ctime>
#include <mutex>

enum class LogLevel {
    DEBUG,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

class Logger {
private:
    std::ofstream log_file;
    std::mutex log_mutex;
    static Logger* instance;
    
    Logger(const std::string& filename) {
        log_file.open(filename, std::ios::app);
    }
    
public:
    ~Logger() {
        if (log_file.is_open()) {
            log_file.close();
        }
    }
    
    // 单例模式
    static Logger* get_instance(const std::string& filename = "app.log") {
        if (instance == nullptr) {
            instance = new Logger(filename);
        }
        return instance;
    }
    
    void log(LogLevel level, const std::string& message) {
        std::lock_guard<std::mutex> lock(log_mutex);
        
        // 获取当前时间
        auto now = std::time(nullptr);
        auto time_info = std::localtime(&now);
        char time_buffer[20];
        std::strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", time_info);
        
        // 日志级别字符串
        const char* level_str[] = {"DEBUG", "INFO", "WARNING", "ERROR", "FATAL"};
        
        // 写入日志文件
        log_file << "[" << time_buffer << "] [" << level_str[static_cast<int>(level)] << "] " 
                 << message << std::endl;
        
        // 错误级别以上同时输出到控制台
        if (level >= LogLevel::ERROR) {
            std::cerr << "[" << time_buffer << "] [" << level_str[static_cast<int>(level)] << "] " 
                     << message << std::endl;
        }
    }
    
    void debug(const std::string& msg) { log(LogLevel::DEBUG, msg); }
    void info(const std::string& msg) { log(LogLevel::INFO, msg); }
    void warning(const std::string& msg) { log(LogLevel::WARNING, msg); }
    void error(const std::string& msg) { log(LogLevel::ERROR, msg); }
    void fatal(const std::string& msg) { log(LogLevel::FATAL, msg); }
};

// 静态成员定义
Logger* Logger::instance = nullptr;

第二章:调试技巧与工具

2.1 基本调试概念

2.1.1 调试的重要性

调试是程序开发中不可或缺的技能:

  • 快速定位和修复bug
  • 理解程序执行流程
  • 验证程序逻辑正确性
  • 性能分析和优化
2.1.2 常见错误类型
// 1. 语法错误(编译时捕获)
int main() {
    int x = 10  // 缺少分号
    return 0;
}

// 2. 运行时错误
void runtime_errors() {
    int arr[5] = {1, 2, 3, 4, 5};
    
    // 数组越界
    int value = arr[10];  // 运行时错误
    
    // 空指针解引用
    int* ptr = nullptr;
    *ptr = 100;  // 段错误
    
    // 内存泄漏
    int* leak = new int[1000];
    // 忘记delete[]
}

// 3. 逻辑错误
int divide(int a, int b) {
    // 逻辑错误:应该是 b != 0
    if (b == 0) {
        return a / b;  // 仍然会执行除法
    }
    return a / b;
}

2.2 使用GDB调试器

2.2.1 GDB基础命令
# 编译时添加调试信息
g++ -g -o program program.cpp

# 启动GDB
gdb program

# GDB常用命令
(gdb) run                    # 运行程序
(gdb) break main            # 设置断点
(gdb) break 25              # 在第25行设置断点
(gdb) next                  # 单步执行(不进入函数)
(gdb) step                  # 单步执行(进入函数)
(gdb) continue              # 继续执行
(gdb) print variable        # 打印变量值
(gdb) display variable      # 持续显示变量
(gdb) info breakpoints      # 查看断点信息
(gdb) delete 1              # 删除断点1
(gdb) backtrace             # 查看调用栈
(gdb) quit                  # 退出GDB
2.2.2 GDB调试示例
// debug_example.cpp
#include <iostream>
#include <vector>

class Calculator {
public:
    int divide(int a, int b) {
        if (b == 0) {
            throw std::runtime_error("除数不能为零");
        }
        return a / b;
    }
    
    int factorial(int n) {
        if (n < 0) return 0;
        if (n == 0) return 1;
        return n * factorial(n - 1);
    }
};

int main() {
    Calculator calc;
    
    try {
        int result = calc.divide(10, 2);
        std::cout << "10 / 2 = " << result << std::endl;
        
        result = calc.divide(10, 0);  // 这里会抛出异常
        std::cout << "10 / 0 = " << result << std::endl;
    }
    catch (const std::exception& e) {
        std::cerr << "错误: " << e.what() << std::endl;
    }
    
    int fact = calc.factorial(5);
    std::cout << "5! = " << fact << std::endl;
    
    return 0;
}

GDB调试会话:

$ g++ -g -o debug_example debug_example.cpp
$ gdb debug_example

(gdb) break main
(gdb) run
(gdb) next
(gdb) step
(gdb) print result
(gdb) display result
(gdb) continue

2.3 内存调试

2.3.1 Valgrind内存检测
# 安装valgrind
sudo apt-get install valgrind

# 使用valgrind检测内存泄漏
valgrind --leak-check=full ./program

# 检测内存错误
valgrind --tool=memcheck ./program
2.3.2 内存调试示例
// memory_debug.cpp
#include <iostream>

void memory_errors() {
    // 1. 内存泄漏
    int* leak = new int[100];
    // 忘记delete[]
    
    // 2. 数组越界
    int* arr = new int[10];
    arr[10] = 100;  // 越界访问
    
    // 3. 重复释放
    int* dup = new int;
    delete dup;
    delete dup;  // 重复释放
    
    // 4. 使用已释放的内存
    int* used = new int;
    delete used;
    *used = 200;  // 使用已释放的内存
    
    delete[] arr;
}

int main() {
    memory_errors();
    return 0;
}

Valgrind输出分析:

$ g++ -g -o memory_debug memory_debug.cpp
$ valgrind --leak-check=full ./memory_debug

2.4 调试宏和断言

2.4.1 调试宏定义
#include <stdio.h>
#include <assert.h>

// 调试宏定义
#ifdef DEBUG
    #define DEBUG_PRINT(fmt, ...) printf("DEBUG: " fmt "\n", ##__VA_ARGS__)
    #define DEBUG_VAR(var) printf("DEBUG: %s = %d\n", #var, var)
    #define FUNCTION_ENTER() printf("进入函数: %s\n", __FUNCTION__)
    #define FUNCTION_EXIT() printf("退出函数: %s\n", __FUNCTION__)
#else
    #define DEBUG_PRINT(fmt, ...)
    #define DEBUG_VAR(var)
    #define FUNCTION_ENTER()
    #define FUNCTION_EXIT()
#endif

// 使用示例
void test_function(int x) {
    FUNCTION_ENTER();
    DEBUG_PRINT("参数 x = %d", x);
    
    int result = x * 2;
    DEBUG_VAR(result);
    
    FUNCTION_EXIT();
}
2.4.2 断言使用
#include <cassert>
#include <iostream>

class Array {
private:
    int* data;
    int size;
    
public:
    Array(int s) : size(s) {
        assert(s > 0);  // 确保数组大小为正数
        data = new int[size];
    }
    
    int& operator[](int index) {
        // 使用断言检查数组越界(调试模式下)
        assert(index >= 0 && index < size);
        return data[index];
    }
    
    int get_size() const { return size; }
    
    ~Array() {
        delete[] data;
    }
};

// 运行时检查(发布版本也有效)
void safe_array_access(Array& arr, int index) {
    if (index < 0 || index >= arr.get_size()) {
        std::cerr << "错误:数组越界访问,索引 " << index 
                  << ",数组大小 " << arr.get_size() << std::endl;
        return;
    }
    std::cout << "arr[" << index << "] = " << arr[index] << std::endl;
}

2.5 性能分析和优化

2.5.1 代码性能分析
#include <chrono>
#include <iostream>
#include <vector>
#include <algorithm>

class PerformanceTimer {
private:
    std::chrono::high_resolution_clock::time_point start_time;
    std::string operation_name;
    
public:
    PerformanceTimer(const std::string& name) 
        : operation_name(name), start_time(std::chrono::high_resolution_clock::now()) {}
    
    ~PerformanceTimer() {
        auto end_time = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
        std::cout << operation_name << " 耗时: " << duration.count() << " 微秒" << std::endl;
    }
};

// 性能测试示例
void performance_test() {
    const int SIZE = 100000;
    std::vector<int> data(SIZE);
    
    // 生成随机数据
    std::generate(data.begin(), data.end(), rand);
    
    {
        PerformanceTimer timer("冒泡排序");
        // 复制数据进行排序测试
        std::vector<int> bubble_data = data;
        
        // 简单的冒泡排序实现
        for (int i = 0; i < bubble_data.size() - 1; i++) {
            for (int j = 0; j < bubble_data.size() - i - 1; j++) {
                if (bubble_data[j] > bubble_data[j + 1]) {
                    std::swap(bubble_data[j], bubble_data[j + 1]);
                }
            }
        }
    }
    
    {
        PerformanceTimer timer("STL排序");
        std::vector<int> stl_data = data;
        std::sort(stl_data.begin(), stl_data.end());
    }
}

第三章:代码规范与最佳实践

3.1 命名规范

3.1.1 命名约定
// 良好的命名示例
class StudentRecordManager {
private:
    int total_students;
    std::vector<Student> student_list;
    
public:
    void addStudent(const Student& new_student);
    Student* findStudentById(int student_id);
    double calculateAverageGrade() const;
    
private:
    bool validateStudentData(const Student& student) const;
};

// 使用命名空间避免冲突
namespace university {
    namespace records {
        class StudentManager {
            // ...
        };
    }
}

// 常量命名
const int MAX_STUDENTS_PER_CLASS = 30;
const double PI_VALUE = 3.14159265359;

// 枚举命名
enum class StudentStatus {
    ACTIVE,
    INACTIVE,
    GRADUATED,
    SUSPENDED
};

3.2 代码组织

3.2.1 头文件结构
// student.h - 头文件
#ifndef STUDENT_H
#define STUDENT_H

#include <string>
#include <vector>

namespace university {
namespace records {

class Student {
private:
    int id;
    std::string name;
    int age;
    std::vector<double> grades;
    
public:
    // 构造函数
    Student(int student_id, const std::string& student_name, int student_age);
    
    // getter方法
    int getId() const { return id; }
    const std::string& getName() const { return name; }
    int getAge() const { return age; }
    
    // 成绩管理
    void addGrade(double grade);
    double getAverageGrade() const;
    
    // 验证函数
    bool isValid() const;
    
private:
    // 私有辅助函数
    bool validateGrade(double grade) const;
};

} // namespace records
} // namespace university

#endif // STUDENT_H
3.2.2 源文件实现
// student.cpp - 实现文件
#include "student.h"
#include <stdexcept>
#include <numeric>
#include <algorithm>

namespace university {
namespace records {

Student::Student(int student_id, const std::string& student_name, int student_age)
    : id(student_id), name(student_name), age(student_age) {
    
    if (!isValid()) {
        throw std::invalid_argument("Invalid student data");
    }
}

void Student::addGrade(double grade) {
    if (!validateGrade(grade)) {
        throw std::out_of_range("Grade must be between 0 and 100");
    }
    grades.push_back(grade);
}

double Student::getAverageGrade() const {
    if (grades.empty()) {
        return 0.0;
    }
    double sum = std::accumulate(grades.begin(), grades.end(), 0.0);
    return sum / grades.size();
}

bool Student::isValid() const {
    return id > 0 && !name.empty() && age > 0 && age < 150;
}

bool Student::validateGrade(double grade) const {
    return grade >= 0.0 && grade <= 100.0;
}

} // namespace records
} // namespace university

3.3 错误处理最佳实践

3.3.1 防御式编程
#include <iostream>
#include <fstream>
#include <string>
#include <stdexcept>

class FileProcessor {
private:
    std::string filename;
    std::ifstream file;
    
public:
    FileProcessor(const std::string& fname) : filename(fname) {
        validateFilename();
    }
    
    ~FileProcessor() {
        if (file.is_open()) {
            file.close();
        }
    }
    
    void openFile() {
        file.open(filename);
        if (!file.is_open()) {
            throw std::runtime_error("无法打开文件: " + filename);
        }
        
        // 检查文件是否为空
        if (file.peek() == std::ifstream::traits_type::eof()) {
            throw std::runtime_error("文件为空: " + filename);
        }
    }
    
    std::string readLine() {
        if (!file.is_open()) {
            throw std::logic_error("文件未打开");
        }
        
        std::string line;
        if (!std::getline(file, line)) {
            if (file.eof()) {
                throw std::runtime_error("到达文件末尾");
            } else {
                throw std::runtime_error("读取文件时发生错误");
            }
        }
        
        return line;
    }
    
    bool hasMoreLines() const {
        return file.is_open() && !file.eof();
    }
    
private:
    void validateFilename() {
        if (filename.empty()) {
            throw std::invalid_argument("文件名不能为空");
        }
        
        if (filename.length() > 255) {
            throw std::invalid_argument("文件名过长");
        }
        
        // 检查文件名是否包含非法字符
        const std::string invalid_chars = "\\/:*?\"<>|";
        if (filename.find_first_of(invalid_chars) != std::string::npos) {
            throw std::invalid_argument("文件名包含非法字符");
        }
    }
};

// 使用示例
defensive_programming_example() {
    try {
        FileProcessor processor("data.txt");
        processor.openFile();
        
        while (processor.hasMoreLines()) {
            std::string line = processor.readLine();
            std::cout << "读取行: " << line << std::endl;
        }
        
        std::cout << "文件处理完成" << std::endl;
    }
    catch (const std::invalid_argument& e) {
        std::cerr << "参数错误: " << e.what() << std::endl;
    }
    catch (const std::runtime_error& e) {
        std::cerr << "运行时错误: " << e.what() << std::endl;
    }
    catch (const std::exception& e) {
        std::cerr << "未知错误: " << e.what() << std::endl;
    }
}

3.4 版本控制基础

3.4.1 Git基础操作
# 初始化Git仓库
git init

# 添加文件到暂存区
git add filename.cpp
git add .  # 添加所有文件

# 提交更改
git commit -m "添加错误处理功能"

# 查看状态
git status

# 查看提交历史
git log --oneline

# 创建分支
git branch feature-error-handling
git checkout feature-error-handling

# 合并分支
git checkout main
git merge feature-error-handling

# 推送到远程仓库
git remote add origin https://github.com/username/repo.git
git push -u origin main

综合练习项目

项目:带错误处理的计算器

// error_handling_calculator.cpp
#include <iostream>
#include <string>
#include <stdexcept>
#include <cmath>
#include <limits>

class CalculatorError : public std::runtime_error {
public:
    CalculatorError(const std::string& message) 
        : std::runtime_error("计算器错误: " + message) {}
};

class Calculator {
private:
    double last_result;
    int operation_count;
    
public:
    Calculator() : last_result(0.0), operation_count(0) {}
    
    double add(double a, double b) {
        validateInput(a);
        validateInput(b);
        last_result = a + b;
        operation_count++;
        return last_result;
    }
    
    double subtract(double a, double b) {
        validateInput(a);
        validateInput(b);
        last_result = a - b;
        operation_count++;
        return last_result;
    }
    
    double multiply(double a, double b) {
        validateInput(a);
        validateInput(b);
        last_result = a * b;
        operation_count++;
        return last_result;
    }
    
    double divide(double a, double b) {
        validateInput(a);
        validateInput(b);
        
        if (std::abs(b) < std::numeric_limits<double>::epsilon()) {
            throw CalculatorError("除数不能为零");
        }
        
        last_result = a / b;
        operation_count++;
        return last_result;
    }
    
    double power(double base, double exponent) {
        validateInput(base);
        validateInput(exponent);
        
        if (base == 0.0 && exponent < 0.0) {
            throw CalculatorError("0的负数次幂无定义");
        }
        
        last_result = std::pow(base, exponent);
        operation_count++;
        return last_result;
    }
    
    double square_root(double value) {
        validateInput(value);
        
        if (value < 0.0) {
            throw CalculatorError("负数没有实数平方根");
        }
        
        last_result = std::sqrt(value);
        operation_count++;
        return last_result;
    }
    
    double getLastResult() const {
        return last_result;
    }
    
    int getOperationCount() const {
        return operation_count;
    }
    
    void reset() {
        last_result = 0.0;
        operation_count = 0;
    }
    
private:
    void validateInput(double value) {
        if (!std::isfinite(value)) {
            throw CalculatorError("输入值必须是有限数");
        }
        
        if (std::abs(value) > 1e308) {
            throw CalculatorError("输入值过大");
        }
    }
};

// 用户界面类
class CalculatorUI {
private:
    Calculator calc;
    
public:
    void run() {
        std::cout << "=== 高级计算器 ===" << std::endl;
        std::cout << "支持操作: +, -, *, /, ^(幂), sqrt(平方根), clear, history, quit" << std::endl;
        
        while (true) {
            try {
                std::string command;
                std::cout << "\n输入命令: ";
                std::cin >> command;
                
                if (command == "quit") {
                    std::cout << "感谢使用计算器!" << std::endl;
                    break;
                }
                else if (command == "clear") {
                    calc.reset();
                    std::cout << "计算器已重置" << std::endl;
                }
                else if (command == "history") {
                    showHistory();
                }
                else if (command == "+") {
                    performBinaryOperation(&Calculator::add, "加法");
                }
                else if (command == "-") {
                    performBinaryOperation(&Calculator::subtract, "减法");
                }
                else if (command == "*") {
                    performBinaryOperation(&Calculator::multiply, "乘法");
                }
                else if (command == "/") {
                    performBinaryOperation(&Calculator::divide, "除法");
                }
                else if (command == "^") {
                    performBinaryOperation(&Calculator::power, "幂运算");
                }
                else if (command == "sqrt") {
                    performUnaryOperation(&Calculator::square_root, "平方根");
                }
                else {
                    std::cout << "未知命令: " << command << std::endl;
                    showHelp();
                }
            }
            catch (const CalculatorError& e) {
                std::cerr << "错误: " << e.what() << std::endl;
                std::cerr << "请检查输入并重试" << std::endl;
            }
            catch (const std::exception& e) {
                std::cerr << "系统错误: " << e.what() << std::endl;
            }
        }
    }
    
private:
    void showHelp() {
        std::cout << "可用命令:" << std::endl;
        std::cout << "  + : 加法运算" << std::endl;
        std::cout << "  - : 减法运算" << std::endl;
        std::cout << "  * : 乘法运算" << std::endl;
        std::cout << "  / : 除法运算" << std::endl;
        std::cout << "  ^ : 幂运算" << std::endl;
        std::cout << "  sqrt : 平方根" << std::endl;
        std::cout << "  clear : 重置计算器" << std::endl;
        std::cout << "  history : 显示历史记录" << std::endl;
        std::cout << "  quit : 退出程序" << std::endl;
    }
    
    void showHistory() {
        std::cout << "操作次数: " << calc.getOperationCount() << std::endl;
        std::cout << "上次结果: " << calc.getLastResult() << std::endl;
    }
    
    void performBinaryOperation(double (Calculator::*op)(double, double), const std::string& op_name) {
        double a, b;
        std::cout << "输入两个数字: ";
        std::cin >> a >> b;
        
        double result = (calc.*op)(a, b);
        std::cout << "结果: " << result << std::endl;
    }
    
    void performUnaryOperation(double (Calculator::*op)(double), const std::string& op_name) {
        double value;
        std::cout << "输入数字: ";
        std::cin >> value;
        
        double result = (calc.*op)(value);
        std::cout << "结果: " << result << std::endl;
    }
};

int main() {
    try {
        CalculatorUI ui;
        ui.run();
    }
    catch (const std::exception& e) {
        std::cerr << "程序运行失败: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

学习评估

理论知识检查

  1. 错误处理机制

    • 理解C语言的errno机制
    • 掌握C++异常处理语法
    • 了解自定义异常类的创建
    • 理解日志记录的重要性
  2. 调试技能

    • 会使用GDB基本命令
    • 能够设置断点和单步执行
    • 掌握内存调试工具的使用
    • 了解性能分析方法
  3. 代码质量

    • 遵循命名规范
    • 编写防御式代码
    • 使用版本控制系统
    • 编写清晰的注释

编程练习

  1. 基础练习

    • 编写带错误检查的文件复制程序
    • 实现简单的日志系统
    • 创建自定义异常类
  2. 进阶练习

    • 使用GDB调试复杂程序
    • 实现内存泄漏检测
    • 编写性能测试工具
  3. 综合项目

    • 完成带错误处理的计算器
    • 添加完整的日志记录
    • 编写单元测试

代码量统计

  • 错误处理示例代码:150行
  • 调试工具示例:100行
  • 计算器项目:200行
  • 总计:450行(超出目标150行)

学习建议

调试技巧

  1. 系统性调试

    • 从错误信息开始分析
    • 逐步缩小问题范围
    • 使用二分法定位bug
  2. 日志策略

    • 记录关键操作
    • 包含时间戳
    • 分级记录(DEBUG/INFO/ERROR)
  3. 测试驱动

    • 先写测试用例
    • 逐步完善功能
    • 持续集成测试

常见陷阱

  1. 忽略错误检查

    • 总是检查返回值
    • 处理异常情况
    • 提供有用的错误信息
  2. 过度使用异常

    • 异常用于异常情况
    • 不要用于正常控制流
    • 保持异常处理简洁
  3. 调试信息泄露

    • 发布版本移除调试信息
    • 不要在生产环境中暴露详细错误
    • 记录安全相关的错误

下一章第六周:综合复习与项目

返回目录基础阶段学习计划

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐