C++作为面向对象语言,其中一个特性就是函数重载(overload)。
在C语言中,如果定义同名函数,以下代码为例:
int func()
{
return 0;
}
int func(int a)
{
return 0;
}
int main()
{
return 0;
}
编译如下:
$ gcc -o test test.c
test.c:9:5: error: redefinition of ‘func’
9 | int func(int a)
| ^~~~
test.c:4:5: note: previous definition of ‘func’ was here
4 | int func()
| ^~~~
提示func这个函数被重复定义,这表明C语言中,函数名是不能重复的。
但上面的代码,用C++来编译:
$ g++ -o test test.cpp
编译通过,没有问题。
所以C++里是可以同名函数的,只要参数列表不同即可,这种机制就叫做函数重载。
有了重载机制可以更灵活的定义函数,将具体调用哪个函数的任务交给编译器,我们只要把功能类似而只是参数不同的函数都使用同一个名字。
除了函数重载,还有运算符重载。指的是,在同一个命名空间内,可以对同样的函数名或操作符名进行多次声明,对每个声明来讲,其使用了不同的函数参数以及拥有不同的定义(实现)。
当调用这种重载的函数或运算符时,根据你使用的函数实参类型和个数,编译器来决定使用最合适的函数定义或操作符定义。
这个编译器判定的过程叫做重载解析,overload resolution。
但要注意的是,只有参数列表可以用来实现函数重载,而返回类型不影响重载。
很明显,函数参数类型信息和个数是调用函数时的输入信息,可以用来判断调用哪个函数合适。
而返回类型是返回执行后的输出信息,没法用来帮助判断应该调用哪个函数。
所以,如果函数名和参数列表相同,只返回值不同,是不能编译通过的。
int func()
{
return 0;
}
void func()
{
}
int main()
{
return 0;
}
编译出错:
$ g++ -o test test.cpp
test.cpp:9:6: error: ambiguating new declaration of ‘void func()’
9 | void func()
| ^~~~
test.cpp:4:5: note: old declaration ‘int func()’
4 | int func()
| ^~~~
-----------------------------
C++中的子类和父类,其内部定义的函数独立的,父类里定义的成员函数不会影响到子类。
class A{
public:
int funcA(){return 0;}
int funcA(int a){return 0;}
// 在同一个类里,仅返回类型不同的函数不能同时定义
// void funcA(){}
};
class B:public A{
public:
// 父类和子类的成员函数定义不会冲突
void funcA(){}
};
int main()
{
return 0;
}
上面的代码能正常编译通过。
$ g++ -o test test.cpp
但如果使用虚函数的话,子类和父类的同名和同参数列表的函数,就会产生关联,要保证返回类型也一样才行。
class A{
public:
virtual int funcA(){return 0;}
int funcA(int a){return 0;}
};
class B:public A{
public:
void funcA(){}
};
int main()
{
return 0;
}
$ g++ -o test test.cpp
test.cpp:16:8: error: conflicting return type specified for ‘virtual void B::funcA()’
16 | void funcA(){}
| ^~~~~
test.cpp:9:15: note: overridden function is ‘virtual int A::funcA()’
9 | virtual int funcA(){return 0;}
| ^~~~~
上面的无参数的funcA成员函数,在父类和子类中都定义了,并且是虚函数,但返回值类型不同,所以就报错。
--------------------
关于操作符重载,
你可以重新定义或重载C++中的大多数内置运算符。因此,程序员也可以使用用户定义的类型的运算符。
重载运算符是具有特殊名称的函数:关键字 "operator",后面是所定义的运算符的符号。像其他函数一样,重载运算符有一个返回类型和一个参数列表。
比如:
Box operator+(const Box&);
上面例子声明了加法运算符,可用于对两个盒子对象执行加运算并返回最终的盒子对象。大多数重载运算符可以被定义为普通的非成员函数或类成员函数。如果我们将上述函数定义为一个类的非成员函数,那么我们必须传递两个实参,每个操作数一个参数,如下所示:
Box operator+(const Box&, const 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;
}
可以重载的操作符如下:
+ | - | * | / | % | ^ |
& | | | ~ | ! | , | = |
< | > | <= | >= | ++ | -- |
<< | >> | == | != | && | || |
+= | -= | /= | %= | ^= | &= |
|= | *= | <<= | >>= | [] | () |
-> | ->* | new | new [] | delete | delete [] |
不可以重载的操作符:
C操作符:
. // 成员访问或点操作符Member access or dot operator
?: // 三元条件操作符 Ternary or conditional operator
sizeof // 对象大小计算操作符 The object size operator
C++操作符:
:: // 域访问操作符 Scope resolution operator
.* // 成员指针操作符 Pointer to member operator
sizeof运算符返回作为操作数(对象或数据类型)的大小。这是由编译器计算的,它在运行时不能被计算。所以我们不能重载它。
参考:
C++ Overloading (Operator and Function)
13.5 — Overloading operators using member functions – Learn C++