C++ 允许在同一作用域中的某个函数和运算符指定多个声明定义,分别称为函数重载和运算符重载。
重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。重载决策是指调用一个重载函数或重载运算符时,编译器通过把所使用的参数类型与声明定义中的参数类型进行比较,决定选用最合适的声明定义的过程。
函数重载
同一作用域内,允许声明功能类似但是形式参数(指参数的个数、类型或者顺序)必须不同的同名函数,重载函数不能只是返回类型不同。比如同名函数 print ( ) 输出不同类型的数据:
class printData
{
public:
void print(int i) {
cout << "整数为: " << i << endl;
}
void print(double f) {
cout << "浮点数为: " << f << endl;
}
void print(char c[]) {
cout << "字符串为: " << c << endl;
}
};
同名函数 print() 会根据传入参数类型来判断采用哪一个重载函数。
运算符重载
C++ 允许重定义或重载大部分内置的运算符。重载的运算符是带有特殊名称的函数,且函数名是由关键字 operator 和其后要重载的运算符符号构成的。
重载运算符有一个返回类型和一个参数列表:
returnType operator+(params lists);
大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。对于运算符重载函数为类的非成员函数时,就需要将需要操作的对象都作为形式参数声明。
可重载运算符/不可重载运算符
可重载运算符
双目算术运算符 | + (加),-(减),*(乘),/(除),% (取模) |
关系运算符 | ==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于) |
逻辑运算符 | ||(逻辑或),&&(逻辑与),!(逻辑非) |
单目运算符 | + (正),-(负),*(指针),&(取地址) |
自增自减运算符 | ++(自增),--(自减) |
位运算符 | | (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移) |
赋值运算符 | =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>= |
空间申请与释放 | new, delete, new[ ] , delete[] |
其他运算符 | ()(函数调用),->(成员访问),,(逗号),[](下标) |
不可重载运算符
成员访问运算符 | . |
成员指针访问运算符 | .* , ->* |
域运算符 | :: |
长度运算符 | sizeof |
条件运算符 | ? : |
预处理符号 | # |
运算符重载实例
二元运算符重载
以+运算符为例(把两个 Box 对象相加):
// 重载 + 运算符,用于把两个 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;
}
一元运算符重载
递增和递减运算符重载
- 递增和递减一般是改变对象的状态,所以一般是重载为成员函数。
- 重载递增递减,一定要和指针的递增递减区分开。因为这里的重载操作的是对象,而不是指针(由于指针是内置类型,指针的递增递减是无法重载的),所以一般情况的递增递减是操作对象内部的成员变量。
以递增运算符++ 为例:
// 重载前缀递增运算符( ++ )
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;
}
前缀运算符先自增,后计算表达式,后缀运算符则相反。前缀形式重载调用 operator ++ () ,后缀形式重载调用 operator ++ (int)。int 在 括号内是为了向编译器说明这是一个后缀形式,而不是表示整数。
关系运算符重载
以 < 为例:
// 重载小于运算符( < )
bool operator <(const Distance& d)
{
if(feet < d.feet)return true;
if(feet == d.feet && inches < d.inches)return true;
return false;
}
输入/输出运算符<<和 >>重载
可以重载流提取运算符和流插入运算符来操作对象等用户自定义的数据类型。在这里,需要把运算符重载函数声明为类的友元函数,这样我们就能不用创建对象而直接调用函数。以流插入运算符 << 为例:
//重载插入运算符 <<
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;
}
赋值运算符重载
重载赋值运算符( = ),用于创建一个对象,比如拷贝构造函数。
// 所需的构造函数
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;
}
当使用 用户自定义类型变量向内置类型变量赋值时,可以使用自定义类型的隐式转换:
#include<iostream>
using namespace std;
class Int{
private:
int n;
public:
Int(int i){
n = i;
{
operator int() // 这里就是隐式转换声明,应注意到它与运算符重载的不同之处
{
return n;
}
};
int main()
{
Int a(5);
int c=a; // 隐式调用转换函数
cout<<c<<endl;
cout<<a<<endl; // 由于未重载Int的<<操作符,将隐式调用转换函数
}
上述代码的输出将为:
5
5
函数调用运算符()重载
函数调用运算符 () 可以被重载用于类的对象。当重载 () 时,您不是创造了一种新的调用函数的方式,相反地,这是创建一个可以传递任意数目参数的运算符函数:
// 重载函数调用运算符
Distance operator()(int a, int b, int c)
{
Distance D;
// 进行随机计算
D.feet = a + c + 10;
D.inches = b + c + 100 ;
return D;
}
// 调用 operator()
D2 = D1(10, 10, 10);
下标运算符[ ] 重载
下标操作符 [] 通常用于访问数组元素。重载该运算符用于增强操作 C++ 数组的功能:
//下标运算运算符[] 重载
int& operator[](int i)
{
if( i > SIZE ){
cout << "索引超过最大值" <<endl;
// 返回第一个元素
return arr[0];
}
return arr[i];
}
类成员访问运算符 -> 重载
类成员访问运算符( -> )可以被重载,但它较为麻烦。它被定义用于为一个类赋予"指针"行为。类成员访问运算符 -> 必须是一个成员函数。如果使用了 -> 运算符,返回类型必须是指针或者是类的对象。类成员访问运算符 -> 通常与指针引用运算符 * 结合使用,用于实现"智能指针"的功能。这些指针是行为与正常指针相似的对象,唯一不同的是,当通过指针访问对象时,它们会执行其他的任务。比如,当指针销毁时,或者当指针指向另一个对象时,会自动删除对象。
间接引用运算符 -> 可被定义为一个一元后缀运算符。也就是说,给出一个类 Ptr,类 Ptr 的对象可用于访问类 X 的成员,使用方式与指针的用法十分相似。例如:d
class Ptr{
// -> 运算符重载
X * operator->();
};
void f(Ptr p )
{
p->m = 10 ;
}
例如,重载类成员访问运算符 ->:
#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;
}