8.2 运算符重载
运算符重载是C++语言中一种重要的特性,它使得我们能够重新定义已有运算符的意义,使其能够用于特定类的对象执行特定的功能。通过运算符重载,可以实现复杂数据类型的加减乘除等操作,极大地增强了C++语言的扩展能力。
8.2.1 运算符重载的几个问题
在讨论运算符重载时,需要了解以下几个常见问题:
允许重载的运算符
以下运算符是允许重载的:
- 算术运算符:
+
,-
,*
,/
,%
,++
,--
- 位操作运算符:
&
,|
,~
,^
,<<
,>>
- 逻辑运算符:
!
,&&
,||
- 比较运算符:
>
,<
,>=
,<=
,==
,!=
- 赋值运算符:
=
,+=
,-=
,*=
,/=
,%=
,&=
,|=
,^=
,<<=
,>>=
- 其他运算符:
[]
,()
,->
,->*
,new
,delete
,new[]
,delete[]
以下运算符不允许重载:::
, .*
, ?:
, .
运算符的优先级和结合性
重载运算符不会改变原运算符的优先级和结合性,也不会改变运算符的操作数个数和语法结构。单目运算符只能重载为单目运算符,双目运算符只能重载为双目运算符。
编译程序如何选择运算符函数
编译程序对运算符重载的选择,遵循函数重载的选择原则,主要根据运算符的操作数的类型、个数和顺序进行选择。
运算符重载的限制
运算符重载有以下限制:
- 不可臆造新的运算符,必须在C++已有的运算符范围内。
- 重载运算符坚持四个“不能改变”:
- 不能改变运算符操作数的个数。
- 不能改变运算符原有的优先级。
- 不能改变运算符原有的结合性。
- 不能改变运算符原有的语法结构。
运算符重载的原则
运算符重载应使程序更加简洁和直观,但不宜过多使用,以免带来理解上的困难。重载运算符含义必须清楚,避免二义性。
8.2.2 运算符重载函数的两种形式
运算符重载函数有两种常见形式:成员函数形式和友元函数形式。这两种形式都可以访问类中的私有成员。
成员函数形式
以下是一个复数类中重载四则运算运算符的例子:
#include <iostream>
using namespace std;
class Complex {
public:
Complex() { real = imag = 0; }
Complex(double r, double i) { real = r; imag = i; }
Complex operator +(const Complex &c);
Complex operator -(const Complex &c);
Complex operator *(const Complex &c);
Complex operator /(const Complex &c);
friend void print(const Complex &c);
private:
double real, imag;
};
Complex Complex::operator +(const Complex &c) {
return Complex(real + c.real, imag + c.imag);
}
Complex Complex::operator -(const Complex &c) {
return Complex(real - c.real, imag - c.imag);
}
Complex Complex::operator *(const Complex &c) {
return Complex(real * c.real - imag * c.imag, real * c.imag + imag * c.real);
}
Complex Complex::operator /(const Complex &c) {
double denominator = c.real * c.real + c.imag * c.imag;
return Complex((real * c.real + imag * c.imag) / denominator,
(imag * c.real - real * c.imag) / denominator);
}
void print(const Complex &c) {
if (c.imag < 0)
cout << c.real << c.imag << "i";
else
cout << c.real << "+" << c.imag << "i";
}
int main() {
Complex c1(2.0, 3.0), c2(4.0, -2.0), c3;
c3 = c1 + c2; cout << "\nc1 + c2 = "; print(c3);
c3 = c1 - c2; cout << "\nc1 - c2 = "; print(c3);
c3 = c1 * c2; cout << "\nc1 * c2 = "; print(c3);
c3 = c1 / c2; cout << "\nc1 / c2 = "; print(c3);
return 0;
}
友元函数形式
将上例改为友元函数形式:
#include <iostream>
using namespace std;
class Complex {
public:
Complex() { real = imag = 0; }
Complex(double r, double i) { real = r; imag = i; }
friend Complex operator +(const Complex &c1, const Complex &c2);
friend Complex operator -(const Complex &c1, const Complex &c2);
friend Complex operator *(const Complex &c1, const Complex &c2);
friend Complex operator /(const Complex &c1, const Complex &c2);
friend void print(const Complex &c);
private:
double real, imag;
};
Complex operator +(const Complex &c1, const Complex &c2) {
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
Complex operator -(const Complex &c1, const Complex &c2) {
return Complex(c1.real - c2.real, c1.imag - c2.imag);
}
Complex operator *(const Complex &c1, const Complex &c2) {
return Complex(c1.real * c2.real - c1.imag * c2.imag,
c1.real * c2.imag + c1.imag * c2.real);
}
Complex operator /(const Complex &c1, const Complex &c2) {
double denominator = c2.real * c2.real + c2.imag * c2.imag;
return Complex((c1.real * c2.real + c1.imag * c2.imag) / denominator,
(c1.imag * c2.real - c1.real * c2.imag) / denominator);
}
void print(const Complex &c) {
if (c.imag < 0)
cout << c.real << c.imag << "i";
else
cout << c.real << "+" << c.imag << "i";
}
int main() {
Complex c1(2.0, 3.0), c2(4.0, -2.0), c3;
c3 = c1 + c2; cout << "\nc1 + c2 = "; print(c3);
c3 = c1 - c2; cout << "\nc1 - c2 = "; print(c3);
c3 = c1 * c2; cout << "\nc1 * c2 = "; print(c3);
c3 = c1 / c2; cout << "\nc1 / c2 = "; print(c3);
return 0;
}
8.2.3 其他运算符的重载举例
重载下标运算符
#include <iostream>
using namespace std;
class CharArray {
public:
CharArray(int l) {
Length = l;
Buff = new char[Length];
}
~CharArray() { delete[] Buff; }
int GetLength() { return Length; }
char& operator [](int i);
private:
int Length;
char *Buff;
};
char& CharArray::operator [](int i) {
static char ch = 0;
if (i < Length && i >= 0)
return Buff[i];
else {
cout << "\nIndex out of range.";
return ch;
}
}
int main() {
CharArray string1(6);
char *string2 = "string";
for (int cnt = 0; cnt < 8; cnt++)
string1[cnt] = string2[cnt];
cout << "\n";
for (int cnt = 0; cnt < 8; cnt++)
cout << string1[cnt];
cout << "\n";
cout << string1.GetLength() << endl;
return 0;
}
重载增1减1运算符
#include <iostream>
using namespace std;
class Counter {
public:
Counter() { v = 0; }
Counter operator ++();
Counter operator ++(int);
void print() { cout << v << endl; }
private:
unsigned v;
};
Counter Counter::operator ++() {
v++;
return *this;
}
Counter Counter::operator ++(int) {
Counter t = *this;
v++;
return t;
}
int main() {
Counter c;
for (int i = 0; i < 8; i++)
++c;
c.print();
for (int i = 0; i < 8; i++)
c++;
c.print();
return 0;
}
说明
- 在该程序中,类
Counter
定义了两个重载的增1运算符函数:Counter operator++()
为前缀运算符,而Counter operator++(int)
为后缀运算符。 - 前缀运算符直接增加计数器的值并返回自身的引用。
- 后缀运算符首先保存当前对象的副本,然后增加计数器的值,最后返回保存的副本。
重载函数调用运算符
#include <iostream>
using namespace std;
class F {
public:
double operator()(double x, double y) const;
};
double F::operator()(double x, double y) const {
return (x + 5) * y;
}
int main() {
F f;
cout << f(1.5, 2.2) << endl;
return 0;
}
说明
- 在该程序中,重载了函数调用运算符
operator()
. - 该运算符实现了一个简单的数学函数
(x + 5) * y
。 - 使用
f(1.5, 2.2)
进行调用时,实际调用的是f.operator()(1.5, 2.2)
。
总结
运算符重载是C++语言中的一个强大特性,能够使程序更加简洁和易读。通过重载运算符,可以实现自定义数据类型的自然操作,如复数的加减乘除、数组的安全访问等。在运算符重载时,需要注意以下几点:
- 重载运算符应保持其原有的优先级和结合性。
- 重载运算符的含义应清晰明确,避免二义性。
- 根据需求选择合适的重载形式,成员函数形式和友元函数形式各有优缺点。
通过合理使用运算符重载,可以极大地提高程序的可读性和可维护性,同时也增强了C++语言的表达能力。