函数重载:
在一个类中,函数名相同,函数参数类型,或者函数参数个数不同,或者都不同,成为函数重载。
函数的重载发生在程序编译阶段,由编译器进行优化和区分。
《高质量C++/C编程指南》已经清晰的列出了重载函数的特性:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual关键字可有可无。
如:
class A
{
public:
A()
{
age = 3;
}
A(int num)
{
age = num;
}
private:
int age;
};
如A()与A(int num)同为A的构造函数,但由于参数不同,则两则互为重载,对于一个实例,编译器会根据参数的个数执行不同的构造函数
函数的隐藏和覆盖只发生在子类和父类之间,函数隐藏发生在编译时期,相对于对象指针为固定偏移,函数覆盖发生在运行时期,基类和子类的虚函数会根据虚函数列表到指向出运行虚函数。
对于父类和子类中同名函数,如果没有virtual关键字修饰,编译器都将子类中的基类同名函数做隐藏处理;如果有virtual关键字修饰,编译器会建立虚函数列表,运行时期,根据虚函数表运行相应的虚函数。
函数隐藏
函数隐藏,是指派生类函数将基类函数给藏起来了,即函数隐藏发生在子类和父类之间。特性为:
- 如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏。
- 如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数别隐藏。
#include <iostream>
using namespace std;
class A
{
public:
void foo()
{
printf("1\n");
}
};
class B :public A
{
public:
void foo()
{
printf("3\n");
}
};
int main(void)
{
A a;
A *p_A = &a;
p_A->foo();//执行成员函数
B b;
B *p_B = &b;
p_B->foo();//执行成员函数
p_A = &b; //p_A指针执行子类,可以访问被隐藏的基类成员函数
p_A->foo();
system("pause");
return 0;
}
输出为1 3 1
执行成员函数的输出为1,3,这里没有问题。B中成员函数foo()与基类A中成员函数foo,函数名相同,且没有virtual关键字,则子类会将基类中的同名函数隐藏,但是,通过用基类指针指向子类,可以访问被隐藏的基类同名函数foo().
同时也说明,除数据区以外,子类会继承基类所有的成员函数,但对于同名的成员函数,子类在实现自身的成员函数后,会将基类的同名函数隐藏(这里的隐藏指不能通过子类型的对象指针访问到基类的隐藏指针,但可以通过基类指针访问到被隐藏的子类指针中的基类指针)
图为子类父类中成员函数在内存中的排布
函数覆盖
函数的覆盖是指派生类函数覆盖基类函数,只作用与派生类函数,其特性为:
- 不同的范围(分别位于派生类与基类);
- 函数名字相同;
- 参数相同;
- 基类函数必须有virtual关键字。
实际上虚函数的作用,就是实现覆盖
函数的覆盖发生在执行时期,对于有虚函数的类,都含有虚函数表,其虚函数表的指针偏移量固定,但表中函数所指向的函数地址不同,运行时期,会根据虚函数表去执行对应的虚函数。
#include<iostream>
using namespace std;
class A
{
public:
void foo()
{
printf("1\n");
}
virtual void fun()
{
printf("2\n");
}
};
class B : public A
{
public:
void foo()
{
printf("3\n");
}
void fun()
{
printf("4\n");
}
};
int main(void)
{
A a;
B b;
A *p = &a;
p->foo(); //访问基类成员函数
p->fun(); //访问基类虚函数
p = &b;
p->foo(); //访问子类成员函数
p->fun(); //访问基类虚函数
B *ptr = (B *)&a;
ptr->foo(); //访问子类成员函数
ptr->fun(); //访问基类成员函数
system("pause");
return 0;
}
输出为1 2 1 4 3 2
基类和子类中的同名函数,根据是否有virtual关键字修饰分为成员函数和虚函数,对于成员函数基类做隐藏处理,对于虚函数,子类重新实现,并更新虚函数表,基类和子类都是通过访问虚函数表间接访问虚函数。
即,对象指针访问成员函数时,会根据指针的类型,偏移确定的间隔,子类可以通过跳转基类的偏移来访问子类中被隐藏的基类同名函数;对象指针访问虚函数时,会根据虚函数表的指向去执行虚函数。