virtual ---虚函数
C++中的virtual关键字的应用场景:
①修饰父类中的函数;②修饰继承性;
一、virtual可修饰的函数和具有继承性
1、注意: 友元函数、构造函数、static静态函数不能用virtual关键字修饰。普通成员函数和析构函数可以用virtual关键字修饰;
2、virtual具有继承性: 父类中定义为virtual的函数在子类中重写的函数也自动成为虚函数;需要注意的是,只有子类的虚函数和父类的虚函数定义完全一样才被认为是虚函数;
二、修饰父类中的函数
修饰父类中的函数主要有三种函数: 普通函数、析构函数和纯虚函数;
1、修饰父类中的普通函数
被修饰的函数成为虚函数,是C++多态的一种实现(多态分为:编译时多态(通过重载实现)和运行时多态(通过虚函数实现)。
可以理解为,父类的指针或者引用指向其派生类(子类)的对象,当使用指针或者引用调用函数的时候会根据具体的对象类型调用对应对象的函数,(需要的条件是:父类的函数用virtual修饰和子类要重写父类的函数)
#include <iostream>
class Father
{
public:
void func1()
{
printf("this is father func1\n");
}
virtual void func2()
{
printf("this is father func2\n");
}
};
class Son : public Father
{
public:
void func1()
{
printf("this is son fun1\n");
}
void func2()
{
printf("this is son fun2\n");
}
}son;
int main()
{
Father* f1 = new Son(); //父类指针指向子类对象
f1->func1(); //这里在编译的时候会直接给出Parent::Function1()的入口地址
f1->func2(); //注意这里,执行的是哪一个的func2;
return 0;
}
运行结果:
this is father func1;
this is son func2;
通过上面的例子可以看出,使用virtual修饰的函数会根据实际对象的类型(子类还是父类)来调用,没有使用virtual修饰的根据初始化指针的类型(初始化定义的指针)来调用;虚函数(重载实现)最关键的特点是"动态联编",它可以在运行时判断指针指向的对象,并自动调用相应的函数。
2、修饰析构函数
修饰析构函数与上面讲到的使用方法和原理相同,虚析构函数在销毁时会调用对象的析构函数,这样就不会出现像有的数据成员没有销毁导致内存泄漏的问题或者程序直接崩溃。
#include <iostream>
class GrandFather
{
public:
GrandFather()
{
printf("construct grandfather\n");
}
~GrandFather()
{
printf("destruct grandfather\n");
}
};
class Father: public GrandFather
{
public:
Father()
{
printf("construct father\n");
}
~Father()
{
printf("destruct father\n");
}
};
class Son: public Father
{
public:
Son()
{
printf("construct son\n");
}
~Son()
{
printf("destruct son\n");
}
};
int main()
{
Father *f = new Son();
delete f;
return 0;
}
运行结果
运行结果:
onstruct grandfather
construct father
construct son
destruct father
destruct grandfather
上面中没有调用son的析构函数,当将Father或者GrandFather其中一个的析构函数修改为virtual后输出就变为了
3、纯虚函数
纯虚函数的定义就是在虚函数的后面加一个=0。定义了纯虚函数的类是一个抽象类,
virtual void func() = 0;
纯虚函数需要注意的是:
1、定义了纯虚函数的类不能够实例化,也就是不能够创建对象;
2、继承了含有纯虚函数的父类的子类如果没有实现纯虚函数也不能够实例化;
4、修饰继承性
假如有这种场景,一个类继承两个或者更多的父类,但是这些父类又有一些共同的父类,会出现什么样的情况?
如果多个类同时继承同一个父类,需要在子类继承父类的时候,在继承的时候加入修饰符virtual,可避免重复调用父类的构造函数(和类名相同)和析构函数(delete);
virtual class Father1:public GrandFather
virtual class Father2: public GrandFather