2.6实践:简易计算器开发

24 阅读3分钟
#include <iostream>
#include <string>
#include <limits>// 操作类型枚举(C++11强类型枚举)
enum class Operation { Add, Subtract, Multiply, Divide, Unknown };
​
// 异常类型枚举
enum class CalcError { DivisionByZero, InvalidInput };
​
// 打印欢迎界面
void print_welcome() {
    std::cout << "========== 简易计算器 ==========\n"
              << "支持运算符: + - * /\n"
              << "输入格式: 数字1 运算符 数字2\n"
              << "示例: 12.5 + 3.14\n"
              << "输入 q 退出程序\n"
              << "================================\n";
}
​
// 解析运算符(C++17 string_view优化)
Operation parse_operator(std::string_view op) {
    if (op == "+") return Operation::Add;
    if (op == "-") return Operation::Subtract;
    if (op == "*") return Operation::Multiply;
    if (op == "/") return Operation::Divide;
    return Operation::Unknown;
}
​
// 执行计算并返回结果(C++17 nodiscard属性)
[[nodiscard]] double calculate(double num1, Operation op, double num2) {
    switch (op) {
        case Operation::Add:      return num1 + num2;
        case Operation::Subtract: return num1 - num2;
        case Operation::Multiply: return num1 * num2;
        case Operation::Divide:
            if (num2 == 0) throw CalcError::DivisionByZero;
            return num1 / num2;
        default:
            throw CalcError::InvalidInput;
    }
}
​
// 处理用户输入(返回是否继续执行)
bool process_input() {
    std::cout << ">>> ";
    
    std::string input;
    if (!std::getline(std::cin, input) || input == "q") {
        return false;
    }
​
    try {
        // 使用string流解析输入
        std::istringstream iss(input);
        double num1, num2;
        std::string op_str;
        
        if (!(iss >> num1 >> op_str >> num2)) {
            throw CalcError::InvalidInput;
        }
​
        // 清理多余输入
        if (iss.rdbuf()->in_avail() != 0) {
            throw CalcError::InvalidInput;
        }
​
        Operation op = parse_operator(op_str);
        double result = calculate(num1, op, num2);
        
        // 设置输出精度
        std::cout.precision(2);
        std::cout << "结果: " << std::fixed << result << "\n";
        
    } catch (CalcError error) {
        switch (error) {
            case CalcError::DivisionByZero:
                std::cerr << "错误:除数不能为零\n";
                break;
            case CalcError::InvalidInput:
                std::cerr << "错误:无效输入格式\n";
                break;
        }
        // 清除错误状态
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
    
    return true;
}
​
int main() {
    print_welcome();
    while (process_input()) {}
    std::cout << "感谢使用计算器!\n";
    return 0;
}

代码亮点解析

  1. 现代C++特性应用
  • enum class强类型枚举提升安全性
  • std::string_view优化字符串处理
  • [[nodiscard]]属性防止结果丢失
  • std::istringstream安全解析输入
  1. 防御性编程策略
  • 完整输入格式校验
  • 除零错误检测
  • 输入流清理机制
  • 异常处理框架
  1. 用户体验优化
  • 友好的欢迎界面
  • 持续交互循环
  • 精确结果输出
  • 明确的错误提示

功能扩展建议

  1. 增强计算能力
// 添加幂运算支持
case Operation::Power:
    return std::pow(num1, num2);
​
// 添加取模运算
case Operation::Mod:
    if (num2 == 0) throw CalcError::DivisionByZero;
    return std::fmod(num1, num2);
  1. 历史记录功能
std::vector<std::string> history;
​
// 在计算成功后添加
history.push_back(std::to_string(num1) + " " + op_str + " " 
                + std::to_string(num2) + " = " + std::to_string(result));
  1. 科学计算模式
void scientific_mode() {
    // 实现三角函数、对数等计算
    // 示例:sin函数计算
    double angle;
    std::cout << "输入角度值: ";
    std::cin >> angle;
    std::cout << "sin(" << angle << ") = " << std::sin(angle * M_PI / 180);
}

常见问题解答

Q:如何处理连续运算(如1+2*3)? A:需要实现以下改进:

  1. 使用逆波兰表达式算法
  2. 添加运算符优先级处理
  3. 实现表达式解析器

Q:如何支持变量存储? A:扩展方案:

std::map<std::string, double> variables;
​
// 输入示例:x = 5
// 存储变量后可在表达式中使用

Q:为什么推荐使用string流解析输入? A:相比直接使用cin的优点:

  1. 更好的错误恢复能力
  2. 支持整行输入处理
  3. 方便格式验证

注意事项

  1. 输入验证要点
  • 检查数值范围(防止溢出)
  • 过滤非法字符
  • 处理多余空格
  • 验证运算符有效性
  1. 精度问题处理
// 比较浮点数应使用epsilon方法
bool is_equal(double a, double b) {
    return std::abs(a - b) < std::numeric_limits<double>::epsilon();
}
  1. 跨平台兼容性
  • Windows换行符处理(\r\n)
  • 本地化数字格式适配(如逗号小数点)
  • 终端编码兼容(支持中文提示)

本计算器项目完整展示了基础语法在实际开发中的综合应用,后续可通过以下方向深化学习:

  1. 学习使用CMake管理项目
  2. 添加单元测试(Google Test)
  3. 实现GUI界面(Qt框架)
  4. 研究表达式解析算法(逆波兰、递归下降)