在同一作用域中的某个函数和运算符,指定多个定义,分别称为函数重载和运算符重载。
重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。
当调用一个重载函数或重载运算符时,编译器通过把 ”所使用的参数类型“ 与 ”定义中的参数类型“ 进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为重载决策。
函数重载
在同一个作用域内,可以声明几个功能类似的同名函数,但它们的形式参数(参数的个数、类型或顺序)必须不同。不能仅通过返回类型的不同来重载函数。
下面的实例中,同名函数 print() 被用于输出不同的数据类型:
#include <iostream>
using namespace std;
class printData
{
public:
void print(int i)
{
cout << "整数为: " << i << endl;
}
/* 重载声明,具有相同名称print,但参数列表和定义(实现)不相同 */
void print(double f)
{
cout << "浮点数为: " << f << endl;
}
void print(char* c)
{
cout << "字符串为: " << c << endl;
}
};
int main(void)
{
printData pd;
pd.print(5); // 输出整数
pd.print(500.263); // 输出浮点数
char c[] = "Hello C++";
pd.print(c); // 输出字符串
return 0;
}
编译和执行上面的代码:
整数为: 5
浮点数为: 500.263
字符串为: Hello C++
运算符重载
重定义或重载大部分 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 operator+(const Box& b) // 重载 + 运算符,用于把两个 Box 对象相加
{
Box box;
/* 把对象Box1和Box2的长宽高分别相加到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,用来接收返回的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
可重载运算符&不可重载运算符
可重载运算符:
双目算术运算符 | + (加),-(减),*(乘),/(除),% (取模) |
关系运算符 | ==(等于),!= (不等于),< (小于),> (大于),<=(小于等于),>=(大于等于) |
逻辑运算符 | ||(逻辑或),&&(逻辑与),!(逻辑非) |
单目运算符 | + (正),-(负),*(指针),&(取地址) |
自增自减运算符 | ++(自增),--(自减) |
位运算符 | | (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移) |
赋值运算符 | =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>= |
空间申请与释放 | new, delete, new[ ] , delete[] |
其他运算符 | ()(函数调用),->(成员访问),,(逗号),[](下标) |
不可重载运算符:
成员访问运算符 | . |
成员指针访问运算符 | .* , ->* |
域运算符 | :: |
长度运算符 | sizeof |
条件运算符 | ?: |
预处理符号 | # |
运算符重载实例
各种运算符重载的实例:
一元运算符重载
一元运算符只对一个操作数进行操作,下面是一元运算符的实例:
- 递增运算符( ++ )和递减运算符( -- )
- 一元减运算符( - )
- 逻辑非运算符( ! )
一元运算符通常出现在所操作的对象的左边,比如 !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 << "FEET: " << feet << " INCHES:" << 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;
}
演示如何重载递增运算符( ++ ),包括前缀和后缀两种用法, 前缀形式重载调用 Check operator ++ () ,后缀形式重载调用 operator ++ (int):
#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
注意:int 在括号内是为了向编译器说明这是一个后缀形式,这个形参是0,作为前置和置的区分,而不是表示整数。递增和递减一般是改变对象的状态,所以一般是重载为成员函数。且重载递增递减,一定要和指针的递增递减区分开。因为这里的重载操作的是对象,而不是指针(由于指针是内置类型,指针的递增递减是无法重载的),所以一般情况的递增递减是操作对象内部的成员变量。
若想要实现 ++(++a) 这种连续自加,需返回其对象的引用,这样才能保证操作的是同一块内存空间或对象,否则就只是单纯的赋值操作,原来的对象并未被修改,如下所示:
Time& operator++ () // 重载前缀递增运算符( ++ )
{
++minutes; // 对象加 1
if(minutes >= 60)
{
++hours;
minutes -= 60;
}
return *this;
}
二元运算符重载
二元运算符需要两个参数,加运算符( + )、减运算符( - )、乘运算符( * )和除运算符( / )都属于二元运算符。
这里演示重载加运算符( + ),其他的类似:
#include<iostream>
using namespace std;
class Box
{
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
public:
Box();
Box(double l,double b,double h);
Box operator+(const Box & box1);
Box operator+(const int b);
friend Box operator+(const int b, Box box1);
void display();
};
Box::Box()
{
length=0;
breadth=0;
height=0;
}
Box::Box(double l,double b,double h)
{
length = l;
breadth = b;
height = h;
}
Box Box::operator +(const Box& box1) //用于对象相加:(Box1+Box2)
{
Box box;
box.length = this->length + box1.length;
box.breadth = this->breadth + box1.breadth;
box.height = this->height + box1.height;
return box;
}
Box Box::operator+(const int b) //用于对象与数相加:(Box1+6)
{
Box box;
box.length = this->length + b;
box.breadth = this->breadth + b;
box.height = this->height + b;
return box;
}
Box operator+(const int b, Box box1) //用于数与对象相加:(Box1+6)
{
return box1+b; //调用第二个重载+的成员函数,相当于 box1.operator+(b);
}
void Box::display()
{
cout << "length: " << length <<endl;
cout << "breadth: " << breadth <<endl;
cout << "height: " << height <<endl;
}
int main ()
{
int m = 1;
Box box1(1.4,2.7,3.9);
Box box2(2.1,3.5,7.3);
Box box3,box4,box5;
box1.display();
box2.display();
box3=box1+box2; //这里可以交换顺序,相当月box3=box1.operator+(box2);
box3.display();
box4=box1+m; //定义了友元函数operator+(const int b,Box box1),所以也可以交换顺序
box4.display();
box5=m+box1;
box5.display();
return 0;
}
执行输出结果为:
length: 1.4
breadth: 2.7
height: 3.9length: 2.1
breadth: 3.5
height: 7.3length: 3.5
breadth: 6.2
height: 11.2length: 2.4
breadth: 3.7
height: 4.9length: 2.4
breadth: 3.7
height: 4.9
关系运算符重载
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;
}
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;
int inches;
public:
Distance()
{
feet = 0;
inches = 0;
}
Distance(int f, int i)
{
feet = f;
inches = i;
}
friend ostream& operator<<(ostream & output, const Distance & D)
{
output << "FEET : " << D.feet << " INCHES : " << 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;
}
执行输出结果为:
Enter the value of object :
3 2
First Distance : FEET : 11 INCHES : 10
Second Distance :FEET : 5 INCHES : 11
Third Distance :FEET : 3 INCHES : 2
赋值运算符重载
可以重载赋值运算符( = ),用于创建一个对象,比如拷贝构造函数。
演示了如何重载赋值运算符:
#include <iostream>
using namespace std;
class Distance
{
private:
int feet;
int inches;
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 << "FEET: " << feet << " INCHES:" << inches << endl;
}
};
int main()
{
Distance D1(5, 6), D2(4, 3);
cout << "First Distance : ";
D1.displayDistance();
cout << "Second Distance :";
D2.displayDistance();
D1 = D2; // 使用赋值运算符
cout << "First Distance :";
D1.displayDistance();
return 0;
}
执行输出结果为:
First Distance : FEET: 5 INCHES:6
Second Distance :FEET: 4 INCHES:3
First Distance :FEET: 4 INCHES:3
函数调用运算符 () 重载
函数调用运算符 () 可以被重载用于类的对象。重载 () 并非是创造了一种新的调用函数的方式,相反,这是创建一个可以传递任意数目参数的运算符函数。
演示了如何重载函数调用运算符 ():
#include <iostream>
using namespace std;
class Distance
{
private:
int feet;
int inches;
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 << "FEET: " << feet << " INCHES:" << inches << endl;
}
};
int main()
{
Distance D1(11, 10), D2;
cout << "First Distance : ";
D1.displayDistance();
D2 = D1(10, 10, 10); // 调用 operator()()
cout << "Second Distance :";
D2.displayDistance();
return 0;
}
执行输出结果为:
First Distance : FEET: 11 INCHES:10
Second Distance :FEET: 30 INCHES: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;
}
执行输出结果为:
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() - 1)
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