1 基本概念
C++运算符重载(operator overloading)是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。通过运算符重载,可以自定义运算符的行为,使其在作用于用户自定义的数据类型时具有特定的意义。
运算符重载只是一种语法上的方便,它并不是创建新的运算符,而是对已有的运算符进行重新定义。重载的运算符仍然保持原有的优先级和结合性,不能改变这些属性。
本质上,运算符重载(operator overloading)只是一种”语法上的方便”,也就是它只是另一种函数调用的方式。运算符重载的语法是使用operator
关键字,后跟要重载的运算符符号。重载的运算符函数可以作为类的成员函数或非成员函数进行定义。作为成员函数时,第一个参数隐式地是*this
指针,表示当前对象。作为非成员函数时,需要显式地指定所有参数。
运算符重载的目的是为了满足自定义类型的运算需求,使得这些类型能够以更直观、易读和符合语法规则的方式进行运算。通过合理地重载运算符,可以提高代码的可读性和可维护性,同时增强代码的表达能力。然而,在使用运算符重载时需要注意避免过度使用,保持逻辑上的合理性和一致性,以避免引入歧义或性能问题。
2 可重载的运算符
几乎 C++ 中所有的运算符都可以重载,但运算符重载的使用时相当受限制的。特别是不能使用 C++ 中当前没有意义的运算符(例如用**求幂)不能改变运算符优先级,不能改变运算符的参数个数。这样的限制有意义,否则,所有这些行为产生的运算符只会混淆而不是澄清寓语意。 C++中可以重载的运算符类型包括:
运算符类型 | 例子 | 描述 |
---|---|---|
算术运算符 | + , - , * , / , % , ++ , -- | 用于执行算术运算的运算符。 |
关系运算符 | == , != , < , > , <= , >= | 用于比较两个值的关系的运算符。 |
逻辑运算符 | && , || ,! | 用于逻辑运算 |
位运算符 | & 、| 、^ 、~ 、<< 、>> 。 | 用于位运算 |
赋值运算符 | = , += , -= , *= , /= , %= , &= , |= 、^= 、<<= 、>>= | 用于赋值运算 |
成员访问运算符 | -> , ->* | 用于访问类的成员或成员指针的运算符。 |
下标运算符 | [] | 用于访问数组或容器元素的运算符。 |
函数调用运算符 | () | 用于调用函数或方法的运算符。 |
类型转换运算符 | static_cast , dynamic_cast , reinterpret_cast , const_cast | 用于执行类型转换的运算符。这些不是直接重载的运算符,但可以在类 |
然而,并不是所有的运算符都可以被重载。例如,.
(成员访问)、.*
(成员指针访问)、::
(域解析)、sizeof
(长度)、?:
(条件)等运算符是不能被重载的。
在重载运算符时,还需要注意以下几点:
-
重载的运算符应保持原有的语义和优先级,不能改变这些属性。
-
重载的运算符函数可以作为类的成员函数或非成员函数进行定义。作为成员函数时,第一个参数隐式地是
*this
指针;作为非成员函数时,需要显式地指定所有参数。 -
重载运算符的目的是为了增强代码的可读性和表达能力,而不是为了创建新的运算符。因此,在使用运算符重载时应谨慎,避免过度使用或滥用。
3 自增/自减(++/--)运算符重载
在C++中,可以通过重载运算符来定义自定义类型上的++
和--
行为。重载需要区分前置和后置的自增(++
)和自减(--
)运算符。其中,前置版本(如++c
)先执行自增/自减操作,然后返回结果。后置版本(如c++
)先返回当前值,然后再执行自增/自减操作。
3.1 前置自增/自减
前置版本(如++c
)先执行自增/自减操作,然后返回结果。前置版本的语法格式如下:
class ClassName
{
// ... 类的其他成员 ...
public:
// 前置自增运算符重载
ClassName& operator++()
{
// 自增逻辑
return *this;
}
// 前置自减运算符重载
ClassName& operator--()
{
// 自减逻辑
return *this;
}
};
注意,前置版本返回的是引用对象。
3.2 后置自增/自减
后置版本(如c++
)先返回当前值,然后再执行自增/自减操作。后置版本的语法格式如下:
class ClassName
{
// ... 类的其他成员 ...
public:
// 后置自增运算符重载
ClassName operator++(int)
{
ClassName temp = *this; // 保存当前对象的状态
// 自增逻辑
return temp; // 返回自增前的对象状态
}
// 后置自减运算符重载
ClassName operator--(int)
{
ClassName temp = *this; // 保存当前对象的状态
// 自减逻辑
return temp; // 返回自减前的对象状态
}
};
注意,后置版本返回的是对象副本。
3.3 综合案例
以下是一个综合案例,在本案例中,我们创建一个表示复数的类Complex
,并为它重载前置和后置的自增自减运算符。
#include <iostream>
class Complex
{
public:
// 构造函数
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i)
{
}
// 获取实部
double GetReal() const
{
return real;
}
// 获取虚部
double GetImag() const
{
return imag;
}
// 设置实部
void SetReal(double r)
{
real = r;
}
// 设置虚部
void SetImag(double i)
{
imag = i;
}
// 输出复数
void Display() const
{
std::cout << "(" << real << ", " << imag << ")" << std::endl;
}
// 前置自增运算符重载
Complex& operator++()
{
++real; // 实部自增
++imag; // 虚部自增
return *this;
}
// 后置自增运算符重载
Complex operator++(int)
{
Complex temp = *this; // 保存当前对象状态
++real; // 实部自增
++imag; // 虚部自增
return temp; // 返回自增前的对象状态
}
// 前置自减运算符重载
Complex& operator--()
{
--real; // 实部自减
--imag; // 虚部自减
return *this;
}
// 后置自减运算符重载
Complex operator--(int)
{
Complex temp = *this; // 保存当前对象状态
--real; // 实部自减
--imag; // 虚部自减
return temp; // 返回自减前的对象状态
}
private:
double real; // 实部
double imag; // 虚部
};
int main()
{
Complex complexNum(1, 2); // 创建一个复数对象 c1,实部为1,虚部为2
// 使用前置自增运算符
++complexNum;
std::cout << "前置自增后,该该复数:";
complexNum.Display(); // 输出: (2, 3)
// 使用后置自增运算符
complexNum++;
std::cout << "后置自增后,该该复数:";
complexNum.Display(); // 输出: (3, 4)
// 使用前置自减运算符
--complexNum;
std::cout << "前置自减后,该该复数:";
complexNum.Display(); // 输出: (2, 3)
// 使用后置自减运算符
complexNum--;
std::cout << "后置自减后,该该复数:";
complexNum.Display(); // 输出: (1, 2)
return 0;
}
3.4 自增/自减注意事项
在C++中,重载自增(++
)和自减(--
)运算符时,需要注意以下事项:
-
返回值类型:
-
前置版本(如
++operator
)应该返回对象的引用(ClassName&
),以便支持链式操作。 -
后置版本(如
operator++
)应该返回对象的一个副本(ClassName
),因为后置版本需要在修改对象之前返回其原始值。
-
-
参数:
-
后置版本通常带有一个未使用的整型参数(通常命名为
dummy
),这主要是为了与前置版本区分开。这个参数在函数调用时被忽略。
-
-
不改变运算符优先级:
-
重载运算符不会改变运算符的优先级。这意味着重载后的运算符应该遵循原有的优先级规则。
-
-
保持原有语义:
-
重载后的运算符应该尽量保持原有语义。例如,重载
++
运算符应该模拟原有加一的语义。
-
4 等于和不等于(==/!=)运算符重载
在C++中,==
和!=
运算符也可以被重载,以便为自定义类型提供相等性比较的逻辑,其语法格式如下:
class ClassName
{
// ... 类的其他成员 ...
public:
// 重载 == 运算符作为成员函数
bool operator==(const ClassName& other) const
{
// 实现比较逻辑并返回结果
}
// 重载 != 运算符作为成员函数(可选,但通常推荐)
bool operator!=(const ClassName& other) const
{
// 通常返回 !(this == other) 的结果
return !(*this == other);
}
// ... 类的其他成员 ...
};
注意,当重载这些运算符时,参数应该是常量引用(const ClassName&
),这样可以避免不必要的对象复制,并且允许比较临时对象和常量对象。同时,这些函数通常也被声明为const
,因为它们不修改调用它们的对象的状态。
#include <iostream>
class Complex
{
public:
// 构造函数
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i)
{
}
// 获取实部
double GetReal() const
{
return real;
}
// 获取虚部
double GetImag() const
{
return imag;
}
// 设置实部
void SetReal(double r)
{
real = r;
}
// 设置虚部
void SetImag(double i)
{
imag = i;
}
// 输出复数
void Display() const
{
std::cout << "(" << real << ", " << imag << ")" << std::endl;
}
// 重载 == 运算符
bool operator==(const Complex& other) const
{
return (real == other.real) && (imag == other.imag);
}
// 重载 != 运算符
bool operator!=(const Complex& other) const
{
return !(*this == other);
}
private:
double real; // 实部
double imag; // 虚部
};
int main()
{
Complex c1(1, 2); // 创建一个复数对象 c1,实部为1,虚部为2
Complex c2(1, 2); // 创建另一个复数对象 c2,与 c1 相等
Complex c3(3, 4); // 创建另一个复数对象 c3,与 c1 不相等
// 使用重载的 == 运算符
if (c1 == c2)
{
std::cout << "c1和c2相同" << std::endl;
} else {
std::cout << "c1和c2不相同" << std::endl;
}
// 使用重载的 != 运算符
if (c1 != c3)
{
std::cout << "c1和c3不相同" << std::endl;
}
else
{
std::cout << "c1和c3相同" << std::endl;
}
return 0;
}
注意,
-
返回值类型:
==
和!=
运算符应该返回布尔值(bool
),表示比较的结果(相等或不相等)。 -
对称性:
==
运算符应该满足对称性,即a == b
和b == a
应该产生相同的结果。!=
运算符则是==
的否定,即a != b
当且仅当a == b
为false
时返回true
。 -
传递性:如果
a == b
和b == c
都为true
,那么a == c
也应该为true
。同样地,如果a != b
和b != c
都为true
,那么a != c
也应该为true
。 -
一致性:如果重载了
==
运算符,通常也应该重载!=
运算符,以保持一致性。
5 函数调用符号()重载
在C++中,operator()
被称为函数调用运算符或调用操作符。当它被重载时,允许类的对象像函数那样被调用。这通常用于创建可调用对象,这些对象可以模拟函数的行为。重载 operator()
可以使对象看起来像是可调用的函数,并允许你以更直观和面向对象的方式封装某些操作。重载operator()
的语法格式如下:
class ClassName
{
public:
// 重载 operator()
ReturnType operator()(ParameterList)
{
// 函数体,实现所需功能
// ReturnType 是你希望 operator() 返回的类型
// ParameterList 是 operator() 所接受的参数列表
}
};
其中,
-
ClassName
是类名 -
ReturnType
是operator()
返回的类型 -
ParameterList
是operator()
所接受的参数列表
#include <iostream>
class Complex
{
private:
double real; // 实部
double imag; // 虚部
public:
// 构造函数
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i)
{
}
// 获取实部
double GetReal() const
{
return real;
}
// 获取虚部
double GetImag() const
{
return imag;
}
// 设置实部
void SetReal(double r)
{
real = r;
}
// 设置虚部
void SetImag(double i)
{
imag = i;
}
// 输出复数
void Display() const
{
std::cout << "(" << real << ", " << imag << ")" << std::endl;
}
// 重载 operator(),执行复数加法
Complex operator()(const Complex& other) const
{
return Complex(real + other.real, imag + other.imag);
}
};
int main()
{
// 创建两个 Complex 对象
Complex c1(1, 2); // (1, 2)
Complex c2(3, 4); // (3, 4)
// 使用重载的 operator() 进行复数加法
// 调用 c1 的 operator(),参数为 c2
Complex result = c1(c2);
// 输出结果
result.Display();
return 0;
}
注意,虽然 operator()
被重载了以模拟函数调用的外观,但它仍然是一个类的成员函数,并且遵循成员函数的调用规则。
欢迎您同步关注我们的微信公众号!!!