1. C++运算符与表达式:构建高效计算逻辑的核心
作为一名有着十多年C++开发经验的老程序员,我深知运算符和表达式是构建高效计算逻辑的基础。今天我想和大家分享一些关于C++运算符和表达式的深入理解,以及我在实际项目中的一些经验总结。
在C++中,运算符和表达式就像是程序员的"工具箱",掌握它们的特性和使用技巧,能让你写出更高效、更优雅的代码。下面我将从基础到进阶,详细讲解C++运算符和表达式的各个方面。
2. 运算符的分类与优先级
2.1 运算符的分类体系
C++运算符可以按照功能分为以下几大类:
-
算术运算符:用于基本的数学运算
- 一元运算符:
+(正号)、-(负号)、++(自增)、--(自减) - 二元运算符:
+(加)、-(减)、*(乘)、/(除)、%(取模)
- 一元运算符:
-
关系运算符:用于比较操作
==(等于)、!=(不等于)<(小于)、<=(小于等于)>(大于)、>=(大于等于)
-
逻辑运算符:用于布尔逻辑运算
!(逻辑非)&&(逻辑与)||(逻辑或)
-
位运算符:用于位级操作
~(按位取反)&(按位与)|(按位或)^(按位异或)<<(左移)、>>(右移)
-
赋值运算符:用于赋值操作
- 基本赋值:
= - 复合赋值:
+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=
- 基本赋值:
-
其他运算符
- 条件运算符:
? : - 逗号运算符:
, - 成员访问运算符:
.、-> - 指针运算符:
*、& - 作用域解析运算符:
:: - 类型转换运算符:
static_cast、dynamic_cast等
- 条件运算符:
2.2 运算符优先级与结合性详解
理解运算符的优先级和结合性对于编写正确的表达式至关重要。下面是一个完整的优先级表(从高到低):
| 优先级 | 运算符类型 | 运算符示例 | 结合性 |
|---|---|---|---|
| 1 | 作用域、成员访问 | :: . -> [] () |
左到右 |
| 2 | 一元运算符 | ! ~ ++ -- + - * & |
右到左 |
| 3 | 乘除模 | * / % |
左到右 |
| 4 | 加减 | + - |
左到右 |
| 5 | 位移 | << >> |
左到右 |
| 6 | 关系比较 | < <= > >= |
左到右 |
| 7 | 相等比较 | == != |
左到右 |
| 8 | 位与 | & |
左到右 |
| 9 | 位异或 | ^ |
左到右 |
| 10 | 位或 | ` | ` |
| 11 | 逻辑与 | && |
左到右 |
| 12 | 逻辑或 | ` | |
| 13 | 条件运算符 | ? : |
右到左 |
| 14 | 赋值运算符 | = += -= *= /= %= 等 |
右到左 |
| 15 | 逗号运算符 | , |
左到右 |
提示:当不确定优先级时,使用括号明确运算顺序是个好习惯。这不仅能让代码更清晰,还能避免潜在的错误。
3. 基本运算符的深入解析
3.1 算术运算符的陷阱与技巧
算术运算符看似简单,但实际使用中有不少需要注意的地方:
cpp复制#include <iostream>
using namespace std;
int main() {
// 整数除法与浮点数除法的区别
int a = 10, b = 3;
cout << "10 / 3 = " << a / b << endl; // 输出3,整数除法会截断小数部分
double c = 10.0, d = 3.0;
cout << "10.0 / 3.0 = " << c / d << endl; // 输出3.33333
// 取模运算的限制
cout << "10 % 3 = " << a % b << endl; // 输出1
// cout << "10.0 % 3.0 = " << c % d << endl; // 错误:%只能用于整数
// 自增自减的前后区别
int x = 5;
cout << "x++ = " << x++ << endl; // 输出5,然后x变为6
cout << "++x = " << ++x << endl; // x先变为7,然后输出7
// 浮点数精度问题
double e = 0.1 + 0.2;
cout << "0.1 + 0.2 = " << e << endl; // 输出0.3?不一定,可能是0.30000000000000004
return 0;
}
经验分享:
- 整数除法会丢弃小数部分,这在很多情况下会导致意外的结果。如果需要保留小数,至少确保其中一个操作数是浮点数。
- 取模运算符
%只能用于整数类型,浮点数取模需要使用fmod函数。 - 自增自减运算符的前置和后置形式有本质区别:前置形式先增减后使用,后置形式先使用后增减。
- 浮点数运算存在精度问题,在需要精确计算的场合(如金融计算)应考虑使用定点数或专门的库。
3.2 关系与逻辑运算符的最佳实践
关系运算符和逻辑运算符是构建条件逻辑的基础,但使用不当会导致难以发现的错误:
cpp复制#include <iostream>
using namespace std;
int main() {
// 关系运算符
int a = 10, b = 20;
cout << boolalpha; // 让cout输出true/false而不是1/0
cout << "a < b: " << (a < b) << endl;
cout << "a > b: " << (a > b) << endl;
cout << "a == b: " << (a == b) << endl;
// 逻辑运算符的短路特性
int x = 5;
(x > 0) && (cout << "x is positive\n"); // 只有x>0为true时才会执行后面的输出
(x < 0) || (cout << "x is not negative\n"); // 只有x<0为false时才会执行后面的输出
// 常见陷阱:赋值与比较混淆
int y = 0;
if (y = 1) { // 这里实际上是赋值,不是比较!但编译器可能不会警告
cout << "y is 1\n";
}
// 防止方法:将常量放在左边
if (1 == y) { // 这样如果写成1 = y会直接编译错误
cout << "y is 1\n";
}
return 0;
}
注意事项:
- 逻辑运算符
&&和||具有短路特性:如果左边的表达式已经能确定整个表达式的结果,右边的表达式将不会被执行。这在某些情况下可以用来简化代码。 - 常见的错误是将比较运算符
==写成赋值运算符=。一个防御性编程技巧是将常量放在比较的左边,这样如果写错编译器会报错。 - 对于复杂的逻辑表达式,适当使用括号可以提高可读性,避免优先级混淆。
3.3 位运算符的高效应用
位运算符在底层编程、性能优化和特定算法中非常有用:
cpp复制#include <iostream>
using namespace std;
void printBinary(int num) {
for (int i = 31; i >= 0; i--) {
cout << ((num >> i) & 1);
if (i % 8 == 0) cout << " ";
}
cout << endl;
}
int main() {
int a = 0b1100; // 12
int b = 0b1010; // 10
cout << "a = "; printBinary(a);
cout << "b = "; printBinary(b);
// 基本位运算
cout << "a & b = "; printBinary(a & b); // 位与:1000 (8)
cout << "a | b = "; printBinary(a | b); // 位或:1110 (14)
cout << "a ^ b = "; printBinary(a ^ b); // 位异或:0110 (6)
cout << "~a = "; printBinary(~a); // 位取反:11111111 11111111 11111111 11110011 (-13)
// 位移运算
cout << "a << 2 = "; printBinary(a << 2); // 110000 (48)
cout << "a >> 1 = "; printBinary(a >> 1); // 0110 (6)
// 实用技巧
// 1. 判断奇偶
int x = 15;
cout << x << " is " << ((x & 1) ? "odd" : "even") << endl;
// 2. 交换两个数
int y = 20;
x ^= y ^= x ^= y; // 等同于 swap(x, y)
cout << "After swap: x=" << x << ", y=" << y << endl;
// 3. 检查是否是2的幂
int z = 16;
cout << z << " is power of 2: " << ((z & (z - 1)) == 0) << endl;
return 0;
}
实用技巧:
- 位运算在嵌入式开发、加密算法、图形处理等领域有广泛应用。
- 左移
<<相当于乘以2的n次方,右移>>相当于除以2的n次方(对于正数)。 - 位运算可以实现很多高效的操作,如快速判断奇偶、交换变量值、检查2的幂等。
- 注意位移运算对于有符号数的行为是实现定义的,通常建议对无符号数使用位移运算。
4. 复合赋值与特殊运算符
4.1 复合赋值运算符的效率优势
复合赋值运算符不仅书写简洁,在某些情况下还能提高效率:
cpp复制#include <iostream>
using namespace std;
class BigObject {
public:
BigObject& operator+=(const BigObject& other) {
// 复杂的加法操作
return *this;
}
};
BigObject operator+(BigObject lhs, const BigObject& rhs) {
lhs += rhs; // 复用+=的实现
return lhs;
}
int main() {
// 基本类型的复合赋值
int a = 10;
a += 5; // 等价于 a = a + 5
cout << "a = " << a << endl;
// 对于复杂对象,复合赋值通常更高效
BigObject obj1, obj2, obj3;
obj1 += obj2; // 直接修改obj1,不创建临时对象
obj3 = obj1 + obj2; // 会创建临时对象
// 复合赋值链式调用
int x = 5, y = 10, z = 15;
x += y += z; // 先执行y += z,然后x += y
cout << "x=" << x << ", y=" << y << ", z=" << z << endl;
return 0;
}
性能考虑:
- 对于基本类型,现代编译器通常能优化简单表达式,复合赋值和普通赋值的性能差异不大。
- 对于复杂对象,复合赋值(
+=,-=等)通常比普通赋值更高效,因为它可以直接修改左操作数而不需要创建临时对象。 - 在实现自定义类型的运算符时,通常先实现复合赋值版本,然后基于它实现普通运算符,可以减少代码重复。
4.2 条件运算符的优雅应用
条件运算符(?:)是C++中唯一的三元运算符,合理使用可以让代码更简洁:
cpp复制#include <iostream>
#include <string>
using namespace std;
string getGrade(int score) {
return score >= 90 ? "A" :
score >= 80 ? "B" :
score >= 70 ? "C" :
score >= 60 ? "D" : "F";
}
int main() {
// 基本用法
int a = 10, b = 20;
int max = (a > b) ? a : b;
cout << "Max: " << max << endl;
// 嵌套条件运算符
int c = 15;
int largest = (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);
cout << "Largest: " << largest << endl;
// 在函数返回值中使用
cout << "Grade for 85: " << getGrade(85) << endl;
// 在初始化中使用
bool condition = true;
string message = condition ? "Success" : "Failure";
cout << message << endl;
// 注意:过度嵌套会降低可读性
// 以下写法虽然合法,但不推荐
int x = 1, y = 2, z = 3;
int result = (x > y) ? (x > z ? x : z) : (y > z ? y : z);
cout << "Result: " << result << endl;
return 0;
}
最佳实践:
- 条件运算符适合简单的条件赋值场景,可以使代码更紧凑。
- 避免过度嵌套条件运算符,这会降低代码可读性。一般来说,嵌套不应超过两层。
- 在初始化列表、return语句等需要表达式的地方,条件运算符特别有用。
- 如果条件分支中的代码较复杂或有多条语句,应该使用if-else结构代替。
4.3 逗号运算符的巧妙用法
逗号运算符,常常被忽视,但在某些场景下非常有用:
cpp复制#include <iostream>
using namespace std;
int main() {
// 基本用法:顺序求值
int a = (1, 2, 3); // a的值为最后一个表达式的结果3
cout << "a = " << a << endl;
// 在for循环中使用
for (int i = 0, j = 10; i < j; i++, j--) {
cout << "i=" << i << ", j=" << j << endl;
}
// 在变量初始化中使用
int x, y, z;
z = (x = 5, y = 10, x + y); // x=5, y=10, z=15
cout << "z = " << z << endl;
// 在lambda表达式中使用
auto func = [](int n) { return (cout << "n=" << n << endl, n * n); };
int result = func(5); // 输出n=5,result=25
cout << "result = " << result << endl;
// 注意:逗号运算符的优先级最低
int b = 10;
int c = b += 5, 20; // 等价于 (b += 5), 20; c=20
cout << "b=" << b << ", c=" << c << endl;
return 0;
}
使用场景:
- for循环中的多变量初始化和更新
- 需要顺序执行多个表达式但只需要最后一个结果的情况
- 在lambda表达式中执行多个操作
- 某些模板元编程和宏定义中
注意事项:
- 逗号运算符的优先级是最低的,必要时需要使用括号明确意图。
- 过度使用逗号运算符会降低代码可读性,应谨慎使用。
- 逗号运算符与函数参数列表中的逗号是不同的概念,后者是语法分隔符而非运算符。
5. 运算符重载的艺术
5.1 运算符重载的基本规则
运算符重载是C++的强大特性,允许我们为自定义类型定义运算符的行为:
cpp复制#include <iostream>
#include <string>
using namespace std;
class Vector2D {
public:
double x, y;
Vector2D(double x = 0, double y = 0) : x(x), y(y) {}
// 成员函数形式重载+
Vector2D operator+(const Vector2D& other) const {
return Vector2D(x + other.x, y + other.y);
}
// 成员函数形式重载+=
Vector2D& operator+=(const Vector2D& other) {
x += other.x;
y += other.y;
return *this;
}
// 成员函数形式重载- (一元)
Vector2D operator-() const {
return Vector2D(-x, -y);
}
// 成员函数形式重载[]
double& operator[](int index) {
if (index == 0) return x;
if (index == 1) return y;
throw out_of_range("Vector2D index out of range");
}
// 友元函数形式重载<<
friend ostream& operator<<(ostream& os, const Vector2D& v) {
return os << "(" << v.x << ", " << v.y << ")";
}
};
// 非成员函数形式重载*
Vector2D operator*(double scalar, const Vector2D& v) {
return Vector2D(scalar * v.x, scalar * v.y);
}
int main() {
Vector2D v1(1, 2), v2(3, 4);
cout << "v1 = " << v1 << endl;
cout << "v2 = " << v2 << endl;
Vector2D sum = v1 + v2;
cout << "v1 + v2 = " << sum << endl;
v1 += v2;
cout << "v1 += v2 → " << v1 << endl;
Vector2D neg = -v1;
cout << "-v1 = " << neg << endl;
Vector2D scaled = 2.5 * v2;
cout << "2.5 * v2 = " << scaled << endl;
cout << "v2[0] = " << v2[0] << ", v2[1] = " << v2[1] << endl;
return 0;
}
重载规则:
- 不能重载的运算符:
.,.*,::,?:,sizeof,typeid - 不能创建新运算符,只能重载已有运算符
- 重载运算符至少有一个操作数是用户定义类型
- 不能改变运算符的优先级和结合性
- 不能改变运算符的操作数个数(一元/二元)
- 某些运算符建议作为成员函数重载(如
=,[],(),->) - 对称运算符通常作为非成员函数重载(如
+,-,*,/等)
5.2 常用运算符重载示例
下面展示一些常见运算符的重载实现:
cpp复制#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;
class String {
char* data;
size_t length;
public:
// 构造函数
String(const char* str = "") {
length = strlen(str);
data = new char[length + 1];
strcpy(data, str);
}
// 拷贝构造函数
String(const String& other) {
length = other.length;
data = new char[length + 1];
strcpy(data, other.data);
}
// 析构函数
~String() {
delete[] data;
}
// 赋值运算符
String& operator=(const String& other) {
if (this != &other) {
delete[] data;
length = other.length;
data = new char[length + 1];
strcpy(data, other.data);
}
return *this;
}
// 下标运算符
char& operator[](size_t index) {
if (index >= length) {
throw out_of_range("Index out of range");
}
return data[index];
}
// 加法运算符(连接字符串)
String operator+(const String& other) const {
String result;
result.length = length + other.length;
result.data = new char[result.length + 1];
strcpy(result.data, data);
strcat(result.data, other.data);
return result;
}
// 比较运算符
bool operator==(const String& other) const {
return strcmp(data, other.data) == 0;
}
bool operator<(const String& other) const {
return strcmp(data, other.data) < 0;
}
// 流输出运算符
friend ostream& operator<<(ostream& os, const String& str) {
return os << str.data;
}
// 函数调用运算符(仿函数)
String operator()(int start, int count) const {
if (start < 0 || start >= length || count < 0 || start + count > length) {
throw out_of_range("Invalid substring range");
}
String result;
result.length = count;
result.data = new char[count + 1];
strncpy(result.data, data + start, count);
result.data[count] = '\0';
return result;
}
};
int main() {
String s1 = "Hello";
String s2 = " World";
// 使用重载的+运算符
String s3 = s1 + s2;
cout << s3 << endl; // 输出 "Hello World"
// 使用重载的[]运算符
s3[0] = 'h';
cout << s3 << endl; // 输出 "hello World"
// 使用重载的比较运算符
if (s1 < s2) {
cout << s1 << " comes before " << s2 << endl;
}
// 使用重载的函数调用运算符
String sub = s3(6, 5);
cout << sub << endl; // 输出 "World"
return 0;
}
设计原则:
- 保持运算符的直观语义,不要给运算符赋予违反直觉的含义
- 对于资源管理类,必须实现拷贝构造函数、赋值运算符和析构函数(三法则)
- 对于容器类,通常需要重载
[]运算符 - 对于函数对象,可以重载
()运算符 - 对于智能指针类,需要重载
*和->运算符 - 流操作通常重载
<<和>>运算符
5.3 类型转换运算符
C++允许定义自定义的类型转换运算符:
cpp复制#include <iostream>
#include <string>
#include <sstream>
using namespace std;
class Rational {
int numerator;
int denominator;
public:
Rational(int num = 0, int den = 1) : numerator(num), denominator(den) {
if (den == 0) {
throw invalid_argument("Denominator cannot be zero");
}
simplify();
}
// 转换为double
operator double() const {
return static_cast<double>(numerator) / denominator;
}
// 转换为string
operator string() const {
ostringstream oss;
oss << numerator;
if (denominator != 1) {
oss << "/" << denominator;
}
return oss.str();
}
// 显式转换为bool
explicit operator bool() const {
return numerator != 0;
}
private:
void simplify() {
int gcd = computeGCD(numerator, denominator);
numerator /= gcd;
denominator /= gcd;
if (denominator < 0) {
numerator = -numerator;
denominator = -denominator;
}
}
int computeGCD(int a, int b) {
return b == 0 ? a : computeGCD(b, a % b);
}
};
int main() {
Rational r1(6, 8); // 3/4
Rational r2(5); // 5/1
// 隐式转换为double
double d = r1;
cout << "As double: " << d << endl; // 0.75
// 隐式转换为string
string s = r1;
cout << "As string: " << s << endl; // "3/4"
// 显式转换为bool
if (static_cast<bool>(r1)) {
cout << "r1 is not zero" << endl;
}
// 在表达式中自动转换
cout << "r1 + 1.5 = " << (r1 + 1.5) << endl; // 2.25
return 0;
}
注意事项:
- 类型转换运算符没有返回类型,函数名就是目标类型
- 隐式类型转换可能导致意外的行为,对于可能引起歧义的转换应该使用
explicit关键字 - 通常应该避免定义过多的类型转换运算符,特别是可能引起歧义的转换路径
- C++11引入了显式类型转换运算符(使用
explicit关键字),可以防止意外的隐式转换
6. 表达式的求值与优化
6.1 表达式求值顺序的陷阱
C++表达式的求值顺序在某些情况下是未指定的,这可能导致微妙的错误:
cpp复制#include <iostream>
using namespace std;
int getValue() {
static int i = 0;
return ++i;
}
void func(int a, int b, int c) {
cout << "a=" << a << ", b=" << b << ", c=" << c << endl;
}
int main() {
// 未指定的求值顺序
func(getValue(), getValue(), getValue());
// 输出可能是1,2,3或3,2,1等,取决于编译器
// 序列点保证的求值顺序
int i = 0;
cout << i++ << ", " << i++ << ", " << i++ << endl;
// 输出可能是0,1,2或2,1,0等,行为未定义!
// 逗号运算符保证的顺序
int j = (getValue(), getValue(), getValue());
cout << "j=" << j << endl; // 一定是3
// 逻辑运算符的短路特性
int* ptr = nullptr;
if (ptr != nullptr && *ptr == 10) { // 安全,因为短路
cout << "Value is 10" << endl;
}
// 未定义行为的例子
i = 0;
int k = i++ + i++; // 未定义行为!
cout << "k=" << k << endl;
return 0;
}
关键点:
- 函数参数的求值顺序是未指定的,不同编译器可能有不同行为
- 修改同一个变量多次而没有序列点的表达式是未定义行为
- 逗号运算符
,、逻辑与&&、逻辑或||等运算符明确规定了求值顺序 - 应该避免编写依赖于特定求值顺序的代码
6.2 表达式优化技术
编写高效的表达式可以显著提升程序性能:
cpp复制#include <iostream>
#include <chrono>
using namespace std;
using namespace chrono;
// 原始版本
double calculateOriginal(int n) {
double result = 0.0;
for (int i = 0; i < n; i++) {
result += 1.0 / (i * i + 1);
}
return result;
}
// 优化版本1:减少重复计算
double calculateOptimized1(int n) {
double result = 0.0;
for (int i = 0; i < n; i++) {
double denominator = i * i + 1;
result += 1.0 / denominator;
}
return result;
}
// 优化版本2:循环展开
double calculateOptimized2(int n) {
double result = 0.0;
int i = 0;
for (; i + 3 < n; i += 4) {
result += 1.0 / (i*i + 1);
result += 1.0 / ((i+1)*(i+1) + 1);
result += 1.0 / ((i+2)*(i+2) + 1);
result += 1.0 / ((i+3)*(i+3) + 1);
}
for (; i < n; i++) {
result += 1.0 / (i*i + 1);
}
return result;
}
// 优化版本3:数学变换
double calculateOptimized3(int n) {
if (n == 0) return 0.0;
if (n == 1) return 1.0;
if (n == 2) return 1.0 + 1.0/5.0;
// 对于大n,使用近似公式
const double pi = 3.141592653589793;
return 0.5 * (pi / tanh(pi) - 1) - 1.0 / (2.0 * n * n);
}
void measurePerformance() {
const int n = 10000000;
const int iterations = 10;
auto test = [n](const string& name, auto func) {
double result = 0.0;
auto start = high_resolution_clock::now();
for (int i = 0; i < iterations; i++) {
result = func(n);
}
auto end = high_resolution_clock::now();
auto duration = duration_cast<milliseconds>(end - start).count();
cout << name << ": result=" << result << ", time=" << duration << "ms" << endl;
};
test("Original", calculateOriginal);
test("Optimized1", calculateOptimized1);
test("Optimized2", calculateOptimized2);
test("Optimized3", calculateOptimized3);
}
int main() {
measurePerformance();
return 0;
}
优化技巧:
- 减少重复计算:将重复的子表达式提取为临时变量
- 利用代数恒等式:用数学等价但计算量更小的表达式替换
- 循环展开:减少循环控制开销(但现代编译器通常能自动完成)
- 强度削弱:用廉价操作替代昂贵操作(如用移位代替乘除)
- 提前计算:将常量表达式移出循环
- 利用短路评估:在逻辑表达式中将最可能短路的部分放在前面
- 选择合适的数据类型:在精度允许的情况下使用更高效的类型
注意事项:
- 优化前应该先确定性能瓶颈,不要过早优化
- 某些优化可能影响代码可读性,需要权衡
- 现代编译器非常智能,很多简单的优化编译器会自动完成
- 在关键路径上的代码才值得手动优化
7. 综合案例:科学计算器实现
7.1 项目架构设计
让我们实现一个功能完整的科学计算器,展示运算符和表达式的实际应用:
code复制ScientificCalculator/
├── include/
│ └── Calculator.h // 计算器核心功能声明
├── src/
│ ├── Calculator.cpp // 计算器核心功能实现
│ └── main.cpp // 用户界面和主程序
└── CMakeLists.txt // 构建配置
7.2 核心功能实现
Calculator.h 头文件定义所有计算功能:
cpp复制#ifndef CALCULATOR_H
#define CALCULATOR_H
#include <cmath>
#include <stdexcept>
#include <string>
#include <vector>
class Calculator {
public:
// 基本运算
static double add(double a, double b);
static double subtract(double a, double b);
static double multiply(double a, double b);
static double divide(double a, double b);
// 科学运算
static double square(double x);
static double squareRoot(double x);
static double power(double base, double exponent);
static double factorial(double x);
static double sine(double x, bool degrees = true);
static double cosine(double x, bool degrees = true);
static double tangent(double x, bool degrees = true);
// 高级运算
static double log10(double x);
static double naturalLog(double x);
static double absolute(double x);
static double roundTo(double x, int decimals = 0);
// 统计运算
static double mean(const std::vector<double>& values);
static double variance(const std::vector<double>& values);
static double stdDev(const std::vector<double>& values);
// 表达式求值
static double evaluate(const std::string& expression);
// 辅助函数
static bool isValidDouble(const std::string& str);
static double stringToDouble(const std::string& str);
static std::vector<double> parseNumbers(const std::string& str);
private:
// 表达式解析辅助函数
static double parseExpression(const std::string& expr, size_t& pos);
static double parseTerm(const std::string& expr, size_t& pos);
static double parseFactor(const std::string& expr, size_t& pos);
static double parseNumber(const std::string& expr, size_t& pos);
static void skipWhitespace(const std::string& expr, size_t& pos);
};
#endif // CALCULATOR_H
Calculator.cpp 实现所有计算功能:
cpp复制#include "Calculator.h"
#include <sstream>
#include <algorithm>
#include <numeric>
using namespace std;
double Calculator::add(double a, double b) { return a + b; }
double Calculator::subtract(double a, double b) { return a - b; }
double Calculator::multiply(double a, double b) { return a * b; }
double Calculator::divide(double a, double b) {
if (b == 0) throw invalid_argument("Division by zero");
return a / b;
}
double Calculator::square(double x) { return x * x; }
double Calculator::squareRoot(double x) {
if (x < 0) throw invalid_argument("Square root of negative number");
return sqrt(x);
}
double Calculator::power(double base,
