我们在学习C++过程中,关于三个词,重载,隐藏,覆盖,那么他们分别做了什么呢?且看下文详解
1:基本概念:
重载:在同一作用域下,函数名相同,但是参数列表不同(参数个数,类型,顺序)的几个函数,构成重载关系。重载不能靠返回值类型来判断。
示例:
class A
{
public:
<1> void show(int i); //1,2,3,4这四个函数都在类A的作用域下且函数名相同,参数列表不同,构成重载
<2> void show(double i);
<3> void show(int i, double j);
<4> void show(double i, int j);
<5> int show(int i); //错误,非重载。注意重载不关心函数返回类型。
};
隐藏:也叫重定义,是指派生类的函数屏蔽了与其同名的基类函数(注意只要同名函数,不管参数列表是否相同,基类函数都会被隐藏)。
具体的来说,就是当如果派生类的函数和基类的函数同名,但是参数不同,此时,不管有无virtual,基类的函数被隐藏。另一点如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字,此时,基类的函数被隐藏。所以在派生类中我们只能访问派生类自己的成员方法,如果我们想要在派生类中访问基类的成员方法,我们就需要加上基类的作用域(因为在派生类中,基类的成员方法也被继承了)。
示例:
class Base
{
public:
void show(); //Base 中的两个函数构成重载
void show(int a);
protected:
int ma;
};
class Derive:public Base
{
public:
void show(); //与Base中的第一个show名字且参数列表相同,但是并没有virtual,所以与Base中的那两个函数都构成隐藏
private:
int mb;
};
int main()
{
Derive b;
b.show(); //调用的是Derive中的show
b.Base::show(); //因为加了作用域,所以就是调用的Base中的不带参数的show
b.Base::show(3); //加了作用域,调用Base中的带整形参数的show
b.show(3); //错误,因为Derive中没有带一个整形参数的show函数
return 0;
}
覆盖:也叫重写,派生类重新定义基类中相同名称和参数的虚函数,函数特征相同,但是具体实现不同,主要是在继承关系中出现的。具体来说就是要构成覆盖就得满足基类中有virtual修饰的函数,而在派生类中有与基类中的虚函数同名且同参数列表的函数,那么派生类中的的该函数就会将基类中的函数覆盖,调用时无法调用基类中的函数。在子类中定义了一个与父类虚函数完全相同的函数,那么这个子类的函数就是重写了父类的虚函数,此时这个子类的函数就是虚函数,即使派生类的这个函数不显示的加上virtual修饰,编译器也会默认为虚函数。(但是我们要知道,其覆盖的是派生类的虚函数表中的函数地址)
示例:
class Base
{
public:
void show(); //Base 中的两个函数构成重载
virtual void show(int a);
protected:
int ma;
};
class Derive:public Base
{
public:
void show(); //并不会发生覆盖,因为基类中的无参show函数并不是虚函数
void show(int b); //与Base中的第二个show函数名字且参数列表相同,并且基类中该函数是虚函数,所以这个函数也会成为虚函数,并且会发生覆盖,将带参的show函数重写
private:
int mb;
};
int main()
{
Derive b;
Base *p=&b; //用基类指针指向派生类对象,但是不能反过来
p->show(); //因为p是Base *类型,所以他会在派生类中查看继承过来的Base部分,就会调用Base中的无参show函数,这是静态绑定,在编译器已经决定了
static_cast<Derive *>(p)->show(); //如果要调用Derive中的函数,就需要将类型强转,当然我们最好用dynamic_cast
p->show(5); //这里却并不会调用Base中的带参show函数,因为基类中的带参show函数是虚函数,在派生类的虚表中已经覆盖掉了。动态绑定
return 0;
}
运行结果:
ps:基类的指针指向派生类对象,指向的是派生类中基类的部分,此时这个基类的指针调用在派生类中对基类中中虚函数重写的虚函数,调用的是派生类的虚函数。 相反,用一个基类的指针指向基类的对象,那么这个基类的指针调用该重写的虚函数,调用的是基类的虚函数,这是因为在基类中只有这一个函数。用派生类的指针调用该函数也调用的是派生类自己的函数。这也即是覆盖的含义,将派生类中的继承来的函数给覆盖掉。
补充:基类的指针能够指向派生类,这是因为派生类由自己的一部分和继承基类部分组成,基类的指针指向派生类其实是指向派生类中的基类部分。相反,派生类的指针无法指向基类。实际上,在虚表中还存在运行时的类型信息,所以我们p是Base*类型,但是我们解引用的*p却是Derive类型,但前提必须是有虚函数哦。