虚函数
虚函数的实现机制
普通的函数是在编译前就已经确定了,这就是所谓的早绑定,称为静态绑定,而虚函数的多态机制却是在编译时来确定具体的函数的。我们将它称之为晚绑定,也叫动态绑定。
当我们用一个父类的指针指向子类的对象时,我们应该是想使用子类自己的函数,这时候我们就可以使用虚函数了。当我们定义了虚函数后,我们类的存储空间内部就有了一个虚函数表指针,它指向着虚函数表。虚函数表里有我们需要调用的函数,子类会直接继承这个虚函数表,如果子类将其中的某个虚函数重写了,那么子类的虚函数表中的对应的父类的虚函数就会被替换。我们要执行哪个函数就是根据这个虚函数表来确定的。
class father {
public:
virtual void fun1() {
cout << "调用father::fun1()" << endl;
}
virtual void fun2() {
cout << "调用father::fun2()" << endl;
}
virtual void fun3() {
cout << "调用father::fun3()" << endl;
}
void fun4() {
cout << "调用father::fun4()" << endl;
}
public:
int x = 100;
int y = 200;
};
class son :public father {
public:
son() {};
virtual void fun1()override//重写父类的虚函数
{
cout << "调用son::fun1()" << endl;
}
};
多继承下的虚函数
顾名思义,多继承下的虚函数的意思是当我们的子类继承自多个父类,而多个父类之中都存在虚函数的情况。
这时我们的子类中也会继承多个虚函数表的,值得一提的是子类中新加的虚函数会加到第一个虚函数表的后面。
消失的析构函数
当我们用父类的指针去析构子类的对象时,如果析构函数不是虚函数,那么就会直接执行父类的析构函数,如果这时子类有从堆中开辟的数据,那就会造成严重的内存泄露。建议如果我们需要使用父类的指针来指向子类的对象这种一般化处理的话,最好还是把析构函数声明成虚函数,以免造成内存泄露。
final和override
final关键字用在类身上是将这个类定义为最终类,意思就是说不能再派生出新的子类。而用在虚函数上面就是声明这个虚函数已经不接受重写了,这就是最终版本,继承自它的子类不能再改写这个虚函数。
override关键字只是给编译器提个醒,声明我们现在是在重写虚函数,而不是声明新的函数。如果我们的声明不能匹配父类的虚函数,编译器就会报警。
纯虚函数和抽象类
如果我们只想定义一个公共的类来派生出多个子类,而这个公共类又没有实际的意义,这个时候我们就可以将公共类的虚函数定义为纯虚函数,纯虚函数是不做具体实现的,具体实现交由子类自己处理。定义了纯虚函数的类我们称之为抽象类。抽象类是不能定义具体的对象的,只能定义指针去指向它的子类。
子类会自动继承这个纯虚函数,如果我们没有重写,那子类会会变成抽象类。