一.函数重载
在同一个作用域下,函数名相同,参数不同(参数的类型,顺序,数量不同)的函数,都可以形成函数的重载.
注意:参数名不同,返回值不同步形成重载.
函数的重载主要用于处理功能相同,类型不同的数据.
int test(int i, int j) {
cout << "test" << endl;
}
int test(int i, double j) {
cout << "test" << endl;
}
int test(int i, int j, int k) {
cout << "test" << endl;
}
C++语言中是支持函数重载的,而C语言却不支持重载.这究竟是什么原因呢?
当有函数被调用的时候,链接器就会到符号表中去查找对应的函数名,来获取函数的地址,再链接到一起.
可以通过查看反汇编代码看看不同编译器的处理.
在C语言中编译器对函数的处理:
由上述截图可知,C语言并没有对函数名进行处理.也就是说无论参数的数量,参数的类型,参数的顺序怎么修改,编译器中函数名都是相同的.因此当链接器到符号表中查询对应的函数名时,就会发现同名函数,引发重定义错误.
在C++语言中编译器对函数的处理:
在C++中,C++对函数名进行了处理,函数以_Z4开头,接着是函数名,最后是所有参数的缩写.
_Z是所有函数的前缀,4是函数名的字符个数,例如第一个_Z4testii则代表函数名为test,具有四个字符,参数分别是ii.
这也就是为什么返回值不同和参数名不构成重载的原因,C++正是通过这种函数名修饰规则来实现函数的重载.
函数重载的缺陷:
- 重载的函数仅仅只是类型不同,代码的复用率比较低,只要有新类型出现时,就需要增加相应的函数
- 代码的可维护性比较低,一个出错可能所有的重载均出错
二.函数隐藏(重定义)
当基类和派生类中出现重名的成员时,派生类就会将基类的同名成员给隐藏起来,然后使用自己的.(但是隐藏并不意味着就无法访问,可以通过声明基类作用域来访问隐藏成员)
class A {
public:
A(int val = 2021)
:_year(val) {
}
void print() {
cout << _year << endl;
}
private:
int _year;
};
class B :public A {
public:
B(int month = 6,int day = 24)
:A()
,_month(month)
,_day(day)
{}
void print() { //与基类的print函数形参隐藏
A::print(); //显式调用基类的隐藏函数
cout << _month << endl;
cout << _day << endl;
}
private:
int _month;
int _day;
};
int main() {
B b;
b.print();
return 0;
}
注意:在基类与派生类中,同名的函数并不能构成重载,因为处于不同的作用域中.而只要函数名相同,就会构成隐藏.
三.虚函数重写(覆盖)
当派生类中有一个和基类完全相同的虚函数(函数名,返回值,参数完全相同),则说明子类的虚函数重写了基类的虚函数(只重写了函数的实现)
class Base {
public:
virtual void show(int n = 10) {
cout << "Base: " << n << endl;
}
};
class Base1 : public Base {
public:
virtual void show(int n = 20) {
cout << "Base1: " << n << endl;
}
};
int main() {
Base* p = new Base1;
p->show();
return 0;
}
//该程序输出:Base1: 10
如果子类重写了缺省值,此时的子类的缺省值是无效的,使用的还是父类的缺省值.
原因是因为多态是动态绑定,而缺省值是静态绑定.对于p,它的静态类型也就是这个指针的类型是Base,所以这里的缺省值是Base的缺省值,而动态类型也就是指向的对象是Base1,所以这里调用的虚函数则是Base1中的虚函数,所以这里就是Base1中的虚函数,Base中的缺省值,也就是Base1: 10.
结论:虚函数的重写只重写函数实现,不重写缺省值.
四.重载,重定义,重写对比
重载:
- 在同一作用域
- 函数名相同,参数的类型,顺序,数量不同
重定义(隐藏):
- 作用域不同,一个在基类一个在派生类
- 函数名相同
- 派生类和基类同名函数如果不构成重写那就是重定义
重写(覆盖):
- 作用域不同,一个在基类,一个在派生类
- 函数名,参数,返回值必须相同(析构函数和协变函数除外)
- 基类和派生类必须都是虚函数(派生类可以不加virtual,基类的虚函数属性可以继承,但是最好要加上virtual)