1、什么是运算符重载?
运算符重载是 C++ 中的一项功能,使运算符(例如 +、- 等)能够处理用户定义的数据类型。这种机制称为编译时多态性,并提供了为不同数据类型定制运算符行为的优点。
例如,我们可以重载“+”运算符来执行整数加法、字符串串联以及复数加法。这增强了运算符的多功能性,使他们能够对更广泛的数据类型进行操作。
2、什么是运算符函数?
运算符函数是一种专门类型的函数,它为特定运算符提供替代实现。它的语法与常规函数类似,但其名称以“operator”关键字开头,后跟运算符符号。
我们可以为同一个运算符定义多个运算符函数,这些函数可以根据它们所使用的操作数的数量和类型来区分。例如,“+”运算符可以针对整数、字符串和复数具有不同的运算符函数实现。这允许对操作员进行定制以满足特定要求。
class ClassName
{
...
public
ReturnType operator OperatorSymbool(argument list)
{
// Implementation logic
}
...
};
ReturnType: 运算符函数返回值的类型。
运算符:编程语言中用于执行特定操作的关键字。
OperatorSymbol:程序中重载的运算符的符号。
参数列表:调用函数时传递给函数的参数列表。
3、使用运算符重载将两个复数相加
假设我们不使用运算符重载并且想要将两个复数相加。我们可以通过创建一个名为“Complex”的复数类来实现这一点。在类内部,我们定义了一个名为“add”的公共方法,它执行两个复数的加法。此方法将接受两个复数作为参数,并将它们相加的结果作为新的复数返回。
class Complex {
public:
Complex add(Complex c1, Complex c2) {
// Perform addition of c1 and c2 and return the result
return result;
}
};
//This approach works.
//But it requires us to call add method
Complex c1, c2, res;
res = c1.add(c1, c2);
众所周知,运算符重载允许我们更改运算符的行为以处理用户定义的数据类型。因此,对于复数重载“+”运算符,我们可以在“Complex”类中定义一个运算符重载函数。
此函数将指定“+”运算符与复数一起使用时的新行为。该函数可以定义为类的成员函数或友元函数,具体取决于运算符及其所操作的操作数的具体要求。
定义完运算符函数后,我们现在可以使用简单的语句将两个复数 c1 和 c2 相加:res = c1 + c2,这相当于 res = c1.operator+ (c2)。这使得代码更直观、更容易理解。
#include <iostream>
using namespace std;
class Complex {
private:
int real, imag;
public:
Complex(int r = 0, int i = 0) {
real = r;
imag = i;
}
Complex operator + (Complex c) {
Complex temp;
temp.real = real + c.real;
temp.imag = imag + c.imag;
return temp;
}
int getReal(){
return real;
}
int getImag(){
return imag;
}
};
int main() {
Complex c1(4, 7);
Complex c2(3, 5);
Complex res;
res = c1 + c2;
cout << "Result: " << res.getReal() << " + " << res.getImag() << "i" << endl;
return 0;
}
//output Result: 7 + 12i
我们还可以将函数签名写成如下:
Complex operator + (const Complex& c)
这个版本与之前的版本有几个显着的区别:
- 参数“c”使用“&”运算符通过引用传递,这意味着该函数将接收到原始对象的直接链接而不是副本。
- 参数“c”也被指定为“const”,表示该函数不能更改它所传递的对象。
这些修改被认为是最佳实践,原因如下:
- 通过引用传递对象,函数可以访问原始对象而无需创建重复对象,从而提高效率并避免创建新对象的不必要的开销。
- 将对象指定为“const”有助于防止对该对象进行意外修改,从而降低代码中出现错误和意外行为的风险。
4、重载加法运算符的另一个例子
class opr
{
private:
int a;
float b;
public:
opr(int a, float b)
{
this->a = a;
this->b = b;
}
opr operator + (opr test)
{
opr tmp(0, 0.0);
tmp.a = a + test.a;
tmp.b = b + test.b;
return tmp;
}
void show()
{
cout << a << " " << b << '\n';
}
};
int main()
{
opr obj1(1, 3.3);
opr obj2(2, 1.5);
opr obj3;
obj3 = obj1 + obj2;
obj3.show();
return 0;
}
5、运算符重载的一些规则
在 C++ 中重载运算符时,需要记住几个重要规则:
1、至少其中一个操作数必须是用户定义的数据类型。
2、只有内置运算符才能重载。这意味着我们无法创建新的运算符,只能更改现有的运算符以使其工作方式不同。
3、重载运算符不能有默认参数,空参数列表“()”除外。
4、重载运算符不会影响其优先级或结合性。
5、操作数的数量无法更改。例如,一元运算符保持一元,二元运算符保持二元。
6、编译器会自动为每个类重载赋值运算符“=”。换句话说,不需要为赋值运算符创建单独的运算符函数。我们可以使用它来复制同一类的对象,类似于使用复制构造函数。
7、重载运算符时,正确且一致地使用它们以使代码更具可读性和易于理解性至关重要。
6、一元运算符的运算符重载
一元运算符是对单个操作数进行操作的运算符。自增运算符“++”和自减运算符“--”是一元运算符的示例。例如,增量运算符“++”将其操作数的值增加1。它可以用作前缀运算符(放置在操作数之前)或后缀运算符(放置在操作数之后)。
例如:
int x = 5;
// x is incremented to 6, and y is set to 6
int y = ++x;
// x is incremented to 7, but z is set to the original value of x (6)
int z = x++;
类似地,减运算符“--”将其操作数的值减1。它也可以用作前缀或后缀运算符。例如:
int x = 5;
// x is decremented to 4, and y is set to 4
int y = --x;
// x is decremented to 3, but z is set to the original value of x (4)
int z = x--;
7、重载自增运算符(++)的实现代码
在下面的代码中,我们定义了一个名为“Value”的类,其中包含一个私有成员变量“count”。我们还定义了一个构造函数,将该成员变量初始化为值 2。在类内部,我们将“operator++”函数定义为成员函数,该函数被重载两次(带和不带 int 参数)。这些函数将“count”成员变量的值加 1。
在 main 函数中,我们创建一个“Value”对象 (v) 并对其多次使用“++”运算符。然后,我们使用“getCount”方法来检索“count”成员变量的值。
class Value
{
private:
int count;
public:
Value() : count(2) {}
// prefix version of ++ operator
void operator ++ ()
{
++count;
}
// postfix version of ++ operator
void operator ++ (int)
{
++count;
}
int getCount()
{
return count;
}
};
int main()
{
Value v;
v++;
cout << v.getCount() << "\n";
++v;
++v;
cout << v.getCount() << "\n";
return 0;
}
//output
//3
//5
一元运算符函数的前缀和后缀版本之间的唯一区别是参数列表。前缀版本不带参数,而后缀版本带一个“int”类型的参数。该参数实际上并不用于传递整数值,而是向编译器发出信号,指示该函数应用于重载运算符的后缀形式。
8、C++中实现运算符重载的三种方法
(1)、通过成员函数重载运算符
成员函数是在类内部定义并作用于该类的对象的函数。关于运算符重载,一元运算符(对单个参数进行操作的运算符)在其列表中没有参数,而二元运算符(对两个参数进行操作的运算符)只有一个参数。
class Complex
{
private:
double real;
double imag;
public:
Complex(double real = 0, double imag = 0) : real(real), imag(imag) {}
// overload the + operator as a member function
Complex operator + (const Complex& other) const
{
return Complex(real + other.real, imag + other.imag);
}
void print() const
{
cout << real << " + " << imag << "i" << endl;
}
};
int main()
{
Complex c1(1, 2);
Complex c2(3, 4);
Complex c3 = c1 + c2;
c3.print();
return 0;
}
(2)、通过友元函数重载运算符
友元函数不是类的成员,但可以直接访问私有和受保护成员,并且可以在类的私有或公共部分中声明。与成员函数相比,它提供了更大的灵活性。
换句话说,如果运算符函数需要访问类的私有和受保护成员,则可以将其定义为友元函数。在这种情况下,一元运算符有一个参数,而二元运算符有两个参数。
class Complex
{
private:
double real;
double imag;
public:
Complex(double real = 0, double imag = 0) : real(real), imag(imag) {}
friend Complex operator + (const Complex& c1, const Complex& c2);
void print()
{
cout << real << " + " << imag << "i" << endl;
}
};
// overload the + operator as a friend function
Complex operator + (const Complex& c1, const Complex& c2)
{
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
int main()
{
Complex c1(1, 2);
Complex c2(3, 4);
Complex c3 = c1 + c2;
c3.print();
return 0;
}
(3)、通过非成员函数重载运算符
非成员函数不是类的成员,无权访问私有成员和受保护成员。
class Complex
{
public:
double real;
double imag;
Complex(double real = 0, double imag = 0) : real(real), imag(imag) {}
void print() const
{
cout << real << " + " << imag << "i" << endl;
}
};
// overload the + operator as a non-member function
Complex operator + (const Complex& c1, const Complex& c2)
{
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
int main()
{
Complex c1(1, 2);
Complex c2(3, 4);
Complex c3 = c1 + c2;
c3.print();
return 0;
}