/*虚函数总是在继承环境中使用,用虚函数实现动态多态性的一般方法如下:
在基类中定义虚函数
在派生类中定义与基类虚函数同名同参数同返回值类型的成员函数 即派生类中的虚函数
虽然基类中的虚函数与个派生类中的虚函数同名同参数,但由于个虚函数的函数体是不同的
因而可用同名虚函数在运行时完成不同对象的操作 从而实现动态绑定*/
#include<iostream>
using namespace std;
class A
{
public:
virtual void foo()
{
cout<<"1"<<endl;
}
void fuu()
{
cout<<"2"<<endl;
}
};
class B:public A
{
public:
void foo()
{
cout <<"3"<<endl;
}
virtual void fuu()
{
cout<<"4"<<endl;
}
};
class C:public B
{
public:
virtual void foo()
{
cout <<"5"<<endl;
}
void fuu()
{
cout<<"6"<<endl;
}
};
class D:public C
{
public:
virtual void foo()
{
cout <<"7"<<endl;
}
void fuu()
{
cout<<"8"<<endl;
}
};
int main()
{
B b;
C c;
D d;
A *pa = &b;
B *pb = &c;
C *pc = &d;
D *pd = &d;
pa->foo();//此函数的调用顺序为 A 中的foo 由于其为虚函数 所以寻找子类的重写函数 foo 输出 3
pa->fuu();//此函数的调用顺序为 A中的fuu 由于其不是虚函数 所以输出2
pb->foo();//此“函数会遵循父类”的调用规则 调用子类中的重写函数 “无论其是不是虚函数” 都将调用 输出5
pb->fuu();//首先会去遵循 父类 的调用规则去寻找 B 中的fuu函数 由于其是虚函数 所以调用 子类的重写函数 fuu 无论是不是虚函数都将调用 输出 6
pc->foo();//遵循父类的调用规则 输出 7
pc->fuu();//遵循父类的调用规则 输出 8
pd->foo();//将自身基类地址赋给自身定义的指针变量 没有子类 是不是虚函数都将输出自身 7
pd->fuu();//将自身基类地址赋给自身定义的指针变量 没有子类 是不是虚函数都将输出自身 8
}
// 3 2 5 6 7 8 7 8
/*调用虚函数的步骤如下 :
1、用基类定义指针变量,如基类“ *p ”
2、将基类对象地址或派生类对象地址赋给该指针变量。如“P = & 基类对象”或者派生类对象
3、 用 ” 指针变量->虚函数(实参)“ 方式去调用基类或派生类中的虚函数,如“p->虚函数(实参)”
限制函数
一个基类中将所有成员都尽可能的设置为虚函数总是有益的(如果该类不是派生类的基类,将成员函数声明为虚函数
是没有意义的)。他除了会增加一些资源开销,没有其他坏处。
在设置虚函数时必须注意以下几点:
1.只有类成员函数才能声明为虚函数 ,这是因为虚函数只试用于有继承关系的类对象所以普通函数不能声明为虚函数
2.静态成员函数不能是虚函数,因为静态函数不受限于某个对象。
3.内联函数不能是虚函数,因为内联函数是不能在运行中动态确定其位置的,即使虚函数类的内部定义,编译时仍将其看作时
仍将其看作是非内联的。
4.构造函数不是虚函数,因为构造时对象还是一片未定型的空间,只有构造完成后,对对象才能成为一个类的名副其实的实例
5.析构函数可以是虚函数,而且同常声明为为虚函数。声明虚析构函数的目的在于:使用delete运算符删除一个对象的时,
能确保析构函数被正确的执行。只是因为,设置虚析构函数后,可以利用动态绑定方式选择析构函数。
*/