C++运算符的重载
什么是运算符的重载
简而言之,就是将运算符定义成一种函数,简化我们的调用。函数的定义类似这种形式 Type operator+(参数列表);
不同运算符的重载
1. 一般的二元运算符
类似于 "+" “-” “*” "/" 的重载实现方式如下 (以下以“ +”的重载为例 )
//我们先定义一个类Complex,以实现复数之间的计算
class Complex
{
public:
Complex()//无参构造函数
{
real = imaginary = 0;
}
Complex(int real, int imaginary)//有参构造函数
{
this->real = real;
this->imaginary = imaginary;
}
string toString() const
{
stringstream ss; //要使用stringstream需要包含 ssstream 头文件
if(real != 0) ss << real;
if(imaginary > 0) ss << " + " << imaginary << "i";
else if(imaginary < 0) ss << " - " << abs(imaginary) << "i"; //要使用abs()函数需要包含 cstdlib 头文件
}
Complex add(const Complex& secondComplex) const; //实现复数的加法(学习了重载之前)
Complex operator+(const Complex& secondComplex) const;//实现复数的加法(学习了重载之后)
private:
int real;//实部
int imaginary;//虚部
};
接下来是对复数加法函数的定义。
Complex Complex::add(const Complex& secondComplex) const //函数末尾的const意为此函数为常成员函数,即不能改变成员变量值的函数
{
return Complex(real + secondComplex.real, imaginary + secondComplex.imaginary);
}
Complex Complex::operator+(const Complex& secondComlex) const
{
return Complex(real + secondComplex.real, imaginary + secondComplex.imaginary);
}
两个函数的内容完全一样,唯一的区别就在于函数头。对于运算符重载函数的调用有两种方式如下:
Complex r1(1, 1), r2(2, 2);//先定义两个Complex类的变量
Complex r3, r4;
r3 = r1.operator+(r2);//调用方法一
r4 = r1 + r2;//调用方法二
调用方法一形式与普通函数的调用方法一致。对于第二种调用方法才能提现出运算符重载的精妙之处,看起来更顺眼了。
接下来来验证输出是否有正确。
Complex r5;
r5 = r1.add(r2);//增加一组老朋友
cout << "老朋友: " << r5.toString() << endl;
cout << "调用方法一结果: " << r3.toString() << endl;
cout << "调用方法二结果: " << r4.toString() << endl;
输出如下
可以看出结果完全一样,符合预期。
2. 重载 [ ] 运算符
对于数组下标运算符 “[ ]” 既可以用于访问对象,也可以用来修改对象。下面来介绍"[ ]"的重载。
如果按照上述的思路,写出来的代码会像下面这样:
注意要在class Complex中声明[ ]重载函数。
//继续沿用上个例子的Complex类
int Complex::operator[](int index) //别忘记声明!!!
{
if(index == 0) return real;
else return imaginary;
}
运行以下代码:
Complex r6(1,3);
cout << "实部是 " << r6[0] << " 虚部是 " << r6[1] << endl;
得到:
看起来结果没有什么问题,但是如果执行以下语句就会出现编译错误
r6[0] = 2;
r6[1] = 3;
那我们该怎么解决这个问题呢?__办法就是将[ ]运算符函数声明为一个引用。__这样是不是就可以实现赋值和访问了呢。
修改后的[ ]重载函数
int& Complex::operator[](int index) //别忘记修改class内容使形式对应
{
if(index == 0) return real;
else return imaginary;
}
重新运行
Complex r6(1, 3);
cout << "实部是 " << r6[0] << " 虚部是 " << r6[1] << endl;
r6[0] = 2;
r6[1] = 3;
cout << "实部是 " << r6[0] << " 虚部是 " << r6[1] << endl;
结果正确。
小总结:将函数声明为一个引用,即函数会返回一个变量的”别名“。修改别名也就相当于修改其本身。
3. 重载简写运算符
对于像 =="+=" “-=” “*=” “/=” 和 “%=“这类的简写运算符,思路跟”[ ]”==类似。下面直接给出代码。
Complex& Complex::operator+=(const Complex& secondComplex)
{
*this = add(secondComplex);
return *this;
}
必须要返回一个引用的原因在于:简写运算符可以作为左值(即被赋值的值)使用,例如:
int x = 0;
(x += 2) += 3;
如果返回值不是引用,那么这两句代码执行后x的值为2。返回引用是为了使该函数可以作为左值。
4. 重载一元运算符
类似于正负数,负数前会有一个 " - " 。我们同样可以对其进行重载。下面给出代码:
Complex Complex::operator-()
{
return Complex(-real, -imaginary);
}
由于在数前加上符号并不会改变数的值,所以该函数无需返回引用。例如:a = 1 -a = -1 但 a = 1 不受影响。
5. 重载 ++ 和 – 运算符
自增、自减有一个不同于其他运算符的特点就是运算符所处的位置不同,带来的结果也不同。那么如何让C++分辨出此类运算符的位置的呢?我们需要使用int型的伪参数。实现代码如下:
//下面把复数的自增定义为实数和虚数各加一
//Prefix increment
Complex& Complex::operator++()
{
real++;
imaginary++;
return *this;
}
//Postfix increment
Complex Complex::operator++(int dummy)
{
Complex tmp(real, imaginary);
real++;
imaginary++;
return tmp;//返回值为自增前的值
}
其中为什么前自增需要返回一个引用而后自增不需要显而易见,因为前自增这个式子的值是他本身的值,即(a++) 的值就是a现在的值。
6. 重载 << 和 >> 运算符
在此之前我们都是需要通过 toString() 来将输出结果转化为字符串,然后以字符串形式输出,那么我们是否可以直接使用 cout << Complex 进行输出呢?
答案显然是肯定的,但需要引入友元函数的概念。
(1) 友元函数
用自己的话说,友元函数即是在一个类A中声明一个属于别处的函数,然后在别处通过这个函数可以访问类A的private成员。
声明友元函数的方法也很简单,只需要在一般声明函数的基础上,在头上加上friend。
例如:
friend void function1(...);
(2) 重载 << and >>
现在有了友元函数的基础,我们可以进行重载函数的书写。
事实上,流插入运算符( << )和流提取运算符( >> )与普通的二元运算符无异。
cout << variable; <=> <<(cout, variable);
cin >> variable; <=> >>(cin, variable);
对于运算符 << 有两个算子,其中的cout是ostream类中的实例,因此,cout不能作为Complex类的成员函数被重载。>> 同理。下面给出友元函数声明的代码:
friend ostream& operator<<(ostream& out, const Complex& complex);
friend istream& operator>>(istream& in, Complex& complex);//由于要赋值,所以不能加const
接着是函数具体代码:
ostream& operator<<(ostream& out, const Complex& complex)
{
if(complex.real != 0) out << complex.real;
if(complex.imaginary > 0) out << " + " << complex.imaginary;
else if(complex.imaginary < 0) out << " - " << abs(complex.imaginary);
return out;
}
istream& operator>>(istream& in, Complex& complex)
{
cout << "Enter Real: ";
in >> complex.real;
cout << "Enter Imaginary: ";
in >> complex.imaginary;
return in;
}
总结
笔者也是刚开始C++的学习,对于运算符重载这一部分的学习。对于引用,友元等方面有了更深的了解。总体而言,难度不是很高。对于其中有几点还是有些没有吃透,比如C++中的 & 和 const的理解还是很浅。对于这些我会尽快学习补漏。