虚函数
虚函数:虚函数是运行时多态,若某个基类函数声明为虚函数,则其公有派生类将定义与其基类虚函数原型相同的函数。那么,当基类指针或基类引用操作派生类对象时,系统会自动用派生类中的同名函数代替基类虚函数
基类定义虚函数后,将会一 虚到底——派生类里的同名函数也为虚函数
虚函数前提:
(1)基类,派生类中拥有原型相同的成员函数
(2)通过virtual关键字,将virtual关键字放在函数前面可将该函数定义为虚函数
通过基类指针指向派生类对象时,调用同名函数时,如果该函数不是虚函数,函数的调用取决于左侧变量的类型;如果该函数时虚函数,函数的调用取决于对象的类型;
如下面这个实例:
#include<iostream>
using namespace std;
class Animal //定义基类
{
public:
//定义speak函数
void speak() {
cout << "Animal language" << endl;
}
};
class Cat :public Animal //定义派生类,且继承
{
public:
//定义speak函数
void speak() {
cout << "cat language miao miao" << endl;
}
};
class Dog
{
public:
void speak() {
cout << "Dog language wang wang" << endl;
}
};
int main()
{
Cat cat;//定义派生类对象
cat.speak();//通过派生类调用speak函数
Animal *panimal = &cat;//定义基类指针指向子类对象
panimal->speak();
Dog dog;
dog.speak();
system("pause");
return 0;
}运行结果:

运行结果:当基类指针指向派生类类对象的时候,调用speak()函数的时候,调用的是基类的函数,这个时候不是虚函数。
当在基类和派生类的speak()函数前加上virtual关键字的时候,再运行代码试试:

加上virtual关键字后,基类指针指向了派生类对象,调用speak()函数的时候,调用的就是派生类的函数了。
这个时候,我们之前这些只是虚函数表面的东西,现在我们在讲讲为什么通过定义虚函数,基类指针就可以直接调用派生类函数了。
虚函数原理
虚函数通过动态联编实现了运行时多态,编译器在执行过程中遇到了virtual关键字,会为这些虚函数的类建立一张虚函数表vtable
在虚函数表中,编译器将按照该虚函数的声明顺序依次保存虚函数地址,同时在每个带有虚函数的类中放置vptr指针,用来指向虚函数表
如图所示:

该图只是一个类的虚函数表,下面我们通过一段代码,来分析他的虚函数表;
class Animal {//基类
public:
//虚函数speak
virtual void speak() { cout << "animal language!" << endl; }
//虚函数sleep
virtual void sleep() { cout << "animal sleep!" << endl; }
};
class Cat :public Animal {
public:
//虚函数 cat_speak
virtual void cat_speak() { cout << "cat language : miao maio" << endl; }
//虚函数cat_sleep
virtual void cat_sleep() { cout << "cat sleep!" << endl; }
};上述代码中,基类和派生类分别有两个虚函数,且基类和派生类中虚函数名称不同,现在分析上述代码的虚函数表:

这是上述代码的虚函数表,现在我们修改上述代码
class Cat :public Animal {
public:
//虚函数 speak
virtual void speak() { cout << "cat language : miao maio" << endl; }
//虚函数cat_sleep
virtual void cat_sleep() { cout << "cat sleep!" << endl; }
};将cat_speak()函数修改与基类同名,这个时候我们再来看看虚函数表:

从该表中,我们知道了,当基类和派生类有同名虚函数的时候,派生类中的虚函数会将基类的虚函数从虚函数表中替换掉。因此我们可以通过基类指针指向派生类对象时,通过基类指针获取到派生类的选函数。
虚析构函数
在C++中,不能声明虚构造函数,可声明虚析构函数,虚析构函数是为了解决基类指针指向派生类对象,并用基类的指针销毁派生类对象的应用产生的。
通常基类指针指向一个new生成的派生对象,通过delete销毁基类指针指向的派生类对象时,会出现两种情况;
(1)如果基类析构函数不是虚析构函数,则只会调用基类的析构函数,派生类的析构函数不被调用,此时派生类中申请的资源不被回收
(2)如果基类中的析构函数是虚析构函数,则释放基类指针指向的对象时会调用基类及派生类析构函数,派生类对象中的所有资源被回收。
纯虚函数
在定义一个表示抽象概念的基类时,有时无法或者不需要给出某些成员函数的具体实现,函数的实现在派生类中完成,基类中这样的函数声明为纯虚函数。
纯虚函数没有函数体,其作用实在基类中为派生类保留一个函数接口,方便派生类根据需要对它实现,实现多态。
纯虚函数声明结构
virtual 函数返回值类型 函数名(参数表) = 0;纯虚函数没有函数体,纯虚函数不可被调用,声明格式后面的"= 0"是以这样的形式说明该函数为纯虚函数。
有兴趣可看一下以下代码
#include<iostream>
using namespace std;
class Animal //定义基类
{
public:
//定义speak函数
virtual void speak() = 0;//定义纯虚函数
};
class Cat :public Animal //定义派生类,且继承
{
public:
//定义speak函数
virtual void speak() {
cout << "cat language miao miao" << endl;
}
};
class Dog
{
public:
virtual void speak() {
cout << "Dog language wang wang" << endl;
}
};
int main()
{
Cat cat;//定义派生类对象
cat.speak();//通过派生类调用speak函数
//cat.Animal::speak();//调用基类同名函数
Dog dog;
dog.speak();
system("pause");
return 0;
}