成员函数被重载的特征
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。
覆盖是指派生类函数覆盖基类函数,特征是
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字。
当派生类对象调用子类中该同名函数时会自动调用子类中的覆盖版本,而不是父类中的被覆盖函数版本,这种机制就叫做覆盖。
派生类对象调用的是派生类的覆盖函数
指向派生类的基类指针调用的也是派生类的覆盖函数
基类的对象调用基类的函数
“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)
f()函数属于覆盖,而g()与h()属于隐藏。从上面的运行结果,我们可以注意到在覆盖中,用基类指针和派生类指针调用函数f()时,系统都是执行的派生类函数f(),而非基类的f(),这样实际上就是完成的“接口”功能。而在隐藏方式中,用基类指针和派生类指针调用函数f()时,系统会进行区分,基类指针调用时,系统执行基类的f(),而派生类指针调用时,系统“隐藏”了基类的f(),执行派生类的f(),这也就是“隐藏”的由来。
指向派生类的基类对象调用的是基类的被隐藏的函数
例:
#include<iostream.h>
class a
{
public:
void f(float x)
{
cout<<"a :: f(float)"<<endl;
}
virtual void g(int x)
{
cout<<"a :: g(int)"<<endl;
}
void h()
{
cout<<"a :: h()"<<endl;
}
};
class b:public a
{
public:
void f()//重载 不可以和a::f(float)重载(不在同一个类)
{
cout<<"b :: f()"<<endl;
}
void f(int x)
{
cout<<"b :: f(int)"<<endl;
}
void g(int x)//覆盖a::g(int x) 参数相同,名称相同,基类有virtual修饰
{
cout<<"b :: g(int)"<<endl;
}
void g(float x)//重载b::g() a::g(int) 名称相同,参数不同
{
cout<<"b :: g(float)"<<endl;
}
void h(float x)//隐藏a::h() 参数不同,名称相同,要调用a::h()必须将其转换为基类
{
cout<<"b :: h(float)"<<endl;
}
};
int main()
{
a *p,z;
b *pb;
b x,y;
p=&x;
z.f(1.5);
z.g(1);
z.h();
cout<<"------------------"<<endl;
p->f(1);
p->g(1);
p->h();
cout<<"------------------"<<endl;
pb=&y;
pb->f(16.1);
pb->f(1);
pb->f();
cout<<"-----------------"<<endl;
pb->g(1);
pb->g(1.5f);
pb->h(1.2);
return 0;
}
a :: f(float)
a :: g(int)
a :: h()
------------------
a :: f(float)
b :: g(int)
a :: h()
------------------
b :: f(int)
b :: f(int)
b :: f()
-----------------
b :: g(int)
b :: g(float)
b :: h(float)
Press any key to continue
虚函数
l 虚函数是动态联编的基础。
l 是非静态的成员函数。
l 在类的声明中,在函数原型之前写virtual。
l virtual 只用来说明类声明中的原型,不能用在函数实现时。
l 具有继承性,基类中声明了虚函数,派生类中无论是否说明,同原型函数都自动为虚函数。
l 本质:不是重载声明而是覆盖。
l 调用方式:通过基类指针或引用,执行时会
根据指针指向的对象的类,决定调用哪个函数。
抽象类
virtual 类型 函数名(参数表)=0; //纯虚函数
带有纯虚函数的类称为抽象类
抽象类作用:
l 抽象类为抽象和设计的目的而建立,将有关的数据和行为组织在一个继承层次结构中,保证派生类具有要求的行为。
l 对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。
注意:
l 抽象类只能作为基类来使用。
l 不能声明抽象类的对象。
l 构造函数不能是虚函数,析构函数可以是虚函数。