您可以重定义或重载大部分 C++ 内置的运算符。这样,您就能使用自定义类型的运算符。
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
Box operator+(const Box&);
声明加法运算符用于把两个 Box 对象相加,返回最终的 Box 对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数,如下所示:
Box operator+(const Box&, const Box&);
下面的实例使用成员函数演示了运算符重载的概念。在这里,对象作为参数进行传递,对象的属性使用 this 运算符进行访问,如下所示:
#include <iostream>
using namespace std;
class Box
{
public:
double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}
void setBreadth( double bre )
{
breadth = bre;
}
void setHeight( double hei )
{
height = hei;
}
// 重载 + 运算符,用于把两个 Box 对象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
// 程序的主函数
int main( )
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 把体积存储在该变量中
// Box1 详述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 详述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// Box1 的体积
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;
// Box2 的体积
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;
// 把两个对象相加,得到 Box3
Box3 = Box1 + Box2;//与Box1.operator+(Box2)等价
// Box3 的体积
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400
Box3 = Box1 + Box2;
其实是Box1.operator+(Box2);
Box1调用以Box2为参数的函数。那为什么是Box1调用这个函数,而不是Box2调用这个函数呢?我们先看一下重载运算符的限制吧。
- c++中的重载运算符可以不是成员函数,但是重载后的运算符必须至少有一个操作数是用户定义的。这将防止用户为标准类型重载运算符。例如你不能为‘+’重载为两个int的差。
- 重载的运算符不能违背原先的语句法则,如你不能把‘/’重载为单目运算符。同样你不能修改运算符的优先级。例如‘*’的优先级大于‘+’
- 不能创建新的运算符
- 不能重载下面表所说的操作符
好了现在来回答上面的问题吧。‘+’是从左向右结合的所以Box1调用这个函数。
那么Box4 = Box1 + Box2+Box3;
可不可以呢?
我们来看看这是怎么被转换成函数调用的。因为‘+’是从左向右结合的所以先被翻译成下面这样
Box1.operator+(Box2+Box3);
然后在翻译成
Box1.operator+(Box2.operator+(Box3));
因为operator+()函数的返回值Box对象所以上面是没有问题的
Tips:
c++标准库中的cout对象的<符号array的[]符号都是通过运算符重载实现的。
可重载运算符/不可重载运算符
下面是可重载的运算符列表:
下面是不可重载的运算符列表:*
除此之外还有sizeof ,typeid,四大c++转换符还有编译器链接连接器使用符号都是不不能重载的。
示例:
一元运算符重载
一元运算符只对一个操作数进行操作,下面是一元运算符的实例:
- 递增运算符( ++ )和递减运算符( – )
- 一元减运算符,即负号( - )
- 逻辑非运算符( ! )
一元运算符通常出现在它们所操作的对象的左边,比如 !obj、-obj 和 ++obj,但有时它们也可以作为后缀,比如 obj++ 或 obj–。
下面的实例演示了如何重载一元减运算符( - )。
#include <iostream>
using namespace std;
class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
// 显示距离的方法
void displayDistance()
{
cout << "F: " << feet << " I:" << inches <<endl;
}
// 重载负运算符( - )
Distance operator- ()
{
feet = -feet;
inches = -inches;
return Distance(feet, inches);
}
};
int main()
{
Distance D1(11, 10), D2(-5, 11);
-D1; // 取相反数
D1.displayDistance(); // 距离 D1
-D2; // 取相反数
D2.displayDistance(); // 距离 D2
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
F: -11 I:-10
F: 5 I:-11。
希望上面的实示例能够帮您更好地理解一元运算符重载的概念,类似地,您可以尝试重载逻辑非运算符( ! )。
二元运算符重载
二元运算符需要两个参数,下面是二元运算符的实例。我们平常使用的加运算符( + )、减运算符( - )、乘运算符( * )和除运算符( / )都属于二元运算符。就像加(+)运算符。
下面的实例演示了如何重载加运算符( + )。类似地,您也可以尝试重载减运算符( - )和除运算符( / )。
#include <iostream>
using namespace std;
class Box
{
double length; // 长度
double breadth; // 宽度
double height; // 高度
public:
double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}
void setBreadth( double bre )
{
breadth = bre;
}
void setHeight( double hei )
{
height = hei;
}
// 重载 + 运算符,用于把两个 Box 对象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
};
// 程序的主函数
int main( )
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 把体积存储在该变量中
// Box1 详述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 详述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// Box1 的体积
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;
// Box2 的体积
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;
// 把两个对象相加,得到 Box3
Box3 = Box1 + Box2;
// Box3 的体积
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400
关系运算符重载
C++ 语言支持各种关系运算符( < 、 > 、 <= 、 >= 、 == 等等),它们可用于比较 C++ 内置的数据类型。
您可以重载任何一个关系运算符,重载后的关系运算符可用于比较类的对象。
下面的实例演示了如何重载 < 运算符,类似地,您也可以尝试重载其他的关系运算符。
#include <iostream>
using namespace std;
class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
// 显示距离的方法
void displayDistance()
{
cout << "F: " << feet << " I:" << inches <<endl;
}
// 重载负运算符( - )
Distance operator- ()
{
feet = -feet;
inches = -inches;
return Distance(feet, inches);
}
// 重载小于运算符( < )
bool operator <(const Distance& d)
{
if(feet < d.feet)
{
return true;
}
if(feet == d.feet && inches < d.inches)
{
return true;
}
return false;
}
};
int main()
{
Distance D1(11, 10), D2(5, 11);
if( D1 < D2 )
{
cout << "D1 is less than D2 " << endl;
}
else
{
cout << "D2 is less than D1 " << endl;
}
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
D2 is less than D1
输入/输出运算符重载
C++ 能够使用流提取运算符 >> 和流插入运算符 << 来输入和输出内置的数据类型。您可以重载流提取运算符和流插入运算符来操作对象等用户自定义的数据类型。
在这里,有一点很重要,我们需要把运算符重载函数声明为类的友元函数,这样我们就能不用创建对象而直接调用函数。
下面的实例演示了如何重载提取运算符 >> 和插入运算符 <<。
#include <iostream>
using namespace std;
class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
friend ostream &operator<<( ostream &output,
const Distance &D )
{
output << "F : " << D.feet << " I : " << D.inches;
return output;
}
friend istream &operator>>( istream &input, Distance &D )
{
input >> D.feet >> D.inches;
return input;
}
};
int main()
{
Distance D1(11, 10), D2(5, 11), D3;
cout << "Enter the value of object : " << endl;
cin >> D3;
cout << "First Distance : " << D1 << endl;
cout << "Second Distance :" << D2 << endl;
cout << "Third Distance :" << D3 << endl;
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
$./a.out
Enter the value of object :
70
10
First Distance : F : 11 I : 10
Second Distance :F : 5 I : 11
Third Distance :F : 70 I : 10
++ 和 – 运算符重载
递增运算符( ++ )和递减运算符( – )是 C++ 语言中两个重要的一元运算符。
下面的实例演示了如何重载递增运算符( ++ ),包括前缀和后缀两种用法。类似地,您也可以尝试重载递减运算符( – )。
#include <iostream>
using namespace std;
class Time
{
private:
int hours; // 0 到 23
int minutes; // 0 到 59
public:
// 所需的构造函数
Time(){
hours = 0;
minutes = 0;
}
Time(int h, int m){
hours = h;
minutes = m;
}
// 显示时间的方法
void displayTime()
{
cout << "H: " << hours << " M:" << minutes <<endl;
}
// 重载前缀递增运算符( ++ )
Time operator++ ()
{
++minutes; // 对象加 1
if(minutes >= 60)
{
++hours;
minutes -= 60;
}
return Time(hours, minutes);
}
// 重载后缀递增运算符( ++ )
Time operator++( int )
{
// 保存原始值
Time T(hours, minutes);
// 对象加 1
++minutes;
if(minutes >= 60)
{
++hours;
minutes -= 60;
}
// 返回旧的原始值
return T;
}
};
int main()
{
Time T1(11, 59), T2(10,40);
++T1; // T1 加 1
T1.displayTime(); // 显示 T1
++T1; // T1 再加 1
T1.displayTime(); // 显示 T1
T2++; // T2 加 1
T2.displayTime(); // 显示 T2
T2++; // T2 再加 1
T2.displayTime(); // 显示 T2
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
H: 12 M:0
H: 12 M:1
H: 10 M:41
H: 10 M:42
赋值运算符重载
就像其他运算符一样,您可以重载赋值运算符( = ),用于创建一个对象,比如拷贝构造函数。
下面的实例演示了如何重载赋值运算符。
#include <iostream>
using namespace std;
class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
void operator=(const Distance &D )
{
feet = D.feet;
inches = D.inches;
}
// 显示距离的方法
void displayDistance()
{
cout << "F: " << feet << " I:" << inches << endl;
}
};
int main()
{
Distance D1(11, 10), D2(5, 11);
cout << "First Distance : ";
D1.displayDistance();
cout << "Second Distance :";
D2.displayDistance();
// 使用赋值运算符
D1 = D2;
cout << "First Distance :";
D1.displayDistance();
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
First Distance : F: 11 I:10
Second Distance :F: 5 I:11
First Distance :F: 5 I:11
函数调用运算符 () 重载
函数调用运算符 () 可以被重载用于类的对象。当重载 () 时,您不是创造了一种新的调用函数的方式,相反地,这是创建一个可以传递任意数目参数的运算符函数。
下面的实例演示了如何重载函数调用运算符 ()。
#include <iostream>
using namespace std;
class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
// 重载函数调用运算符
Distance operator()(int a, int b, int c)
{
Distance D;
// 进行随机计算
D.feet = a + c + 10;
D.inches = b + c + 100 ;
return D;
}
// 显示距离的方法
void displayDistance()
{
cout << "F: " << feet << " I:" << inches << endl;
}
};
int main()
{
Distance D1(11, 10), D2;
cout << "First Distance : ";
D1.displayDistance();
D2 = D1(10, 10, 10); // invoke operator()
cout << "Second Distance :";
D2.displayDistance();
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
First Distance : F: 11 I:10
Second Distance :F: 30 I:120
下标运算符 [] 重载
下标操作符 [] 通常用于访问数组元素。重载该运算符用于增强操作 C++ 数组的功能。
下面的实例演示了如何重载下标运算符 []。
#include <iostream>
using namespace std;
const int SIZE = 10;
class safearay
{
private:
int arr[SIZE];
public:
safearay()
{
register int i;
for(i = 0; i < SIZE; i++)
{
arr[i] = i;
}
}
int& operator[](int i)
{
if( i > SIZE )
{
cout << "索引超过最大值" <<endl;
// 返回第一个元素
return arr[0];
}
return arr[i];
}
};
int main()
{
safearay A;
cout << "A[2] 的值为 : " << A[2] <<endl;
cout << "A[5] 的值为 : " << A[5]<<endl;
cout << "A[12] 的值为 : " << A[12]<<endl;
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
g++−otesttest.cpp
./test
A[2] 的值为 : 2
A[5] 的值为 : 5
A[12] 的值为 : 索引超过最大值
0
类成员访问运算符 -> 重载
类成员访问运算符( -> )可以被重载,但它较为麻烦。它被定义用于为一个类赋予”指针”行为。运算符 -> 必须是一个成员函数。如果使用了 -> 运算符,返回类型必须是指针或者是类的对象。
运算符 -> 通常与指针引用运算符 * 结合使用,用于实现”智能指针”的功能。这些指针是行为与正常指针相似的对象,唯一不同的是,当您通过指针访问对象时,它们会执行其他的任务。比如,当指针销毁时,或者当指针指向另一个对象时,会自动删除对象。
间接引用运算符 -> 可被定义为一个一元后缀运算符。也就是说,给出一个类:
class Ptr{
//…
X * operator->();
};
类 Ptr 的对象可用于访问类 X 的成员,使用方式与指针的用法十分相似。例如:
void f(Ptr p )
{
p->m = 10 ; // (p.operator->())->m = 10
}
语句 p->m 被解释为 (p.operator->())->m。同样地,下面的实例演示了如何重载类成员访问运算符 ->。
#include <iostream>
#include <vector>
using namespace std;
// 假设一个实际的类
class Obj {
static int i, j;
public:
void f() const { cout << i++ << endl; }
void g() const { cout << j++ << endl; }
};
// 静态成员定义
int Obj::i = 10;
int Obj::j = 12;
// 为上面的类实现一个容器
class ObjContainer {
vector<Obj*> a;
public:
void add(Obj* obj)
{
a.push_back(obj); // 调用向量的标准方法
}
friend class SmartPointer;
};
// 实现智能指针,用于访问类 Obj 的成员
class SmartPointer {
ObjContainer oc;
int index;
public:
SmartPointer(ObjContainer& objc)
{
oc = objc;
index = 0;
}
// 返回值表示列表结束
bool operator++() // 前缀版本
{
if(index >= oc.a.size()) return false;
if(oc.a[++index] == 0) return false;
return true;
}
bool operator++(int) // 后缀版本
{
return operator++();
}
// 重载运算符 ->
Obj* operator->() const
{
if(!oc.a[index])
{
cout << "Zero value";
return (Obj*)0;
}
return oc.a[index];
}
};
int main() {
const int sz = 10;
Obj o[sz];
ObjContainer oc;
for(int i = 0; i < sz; i++)
{
oc.add(&o[i]);
}
SmartPointer sp(oc); // 创建一个迭代器
do {
sp->f(); // 智能指针调用
sp->g();
} while(sp++);
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
10
12
11
13
12
14
13
15
14
16
15
17
16
18
17
19
18
20
19
21
运算符重载和友元
假如我们在一开始的Box案例中又重载了一个符号,如下
Box operator*(int i)
{
Box box;
box.length = this->length * i
box.breadth = this->breadth + i;
box.height = this->height + i;
return box;
}
进行如下操作
box =box1*10;
因为*也是从左向右运算运算的。所以将被转换为
box=box1.operator*(10);//ok
但是下面会怎么样呢?
box =10*box1;//error
解决这一问题的方法就是使用非类成员函数的友元函数。
我们只需要在类的外面定义运算符重载函数。然后把他声明为类的友元就可以解决问题了
Box类中
class Box{
public :
friend operator*(int,class Box);
//其他相同
};
类外
Box & operator*(int i,Box & box)
{
box.length * i
box.breadth + i;
box.height + i;
return box;
`
}
Tips:
c++标准库中有很多运算符重载都是使用这种方法实现的。还有一点就是函数参数中至少要有一个用户定义的类型,不然就违反运算符重载的约定了。